diff --git a/README.md b/README.md index 56abe3a7..52dc4899 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,7 @@ Features - Built in caching - Persistent caching support - Suspense(experimental) support +- Retry functionality Usage ----- @@ -90,6 +91,7 @@ Usage
  • useFetch + Suspense
  • useFetch + Pagination + Provider
  • useFetch + Request/Response Interceptors + Provider
  • +
  • useFetch + retryOn, retryDelay
  • useQuery - GraphQL
  • @@ -200,6 +202,8 @@ const App = () => (
    Suspense Mode (auto managed state) +Can put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B). + ```js import useFetch, { Provider } from 'use-http' @@ -207,7 +211,7 @@ function Todos() { const { data: todos } = useFetch({ path: '/todos', data: [], - suspense: true // can put it in 2 places. Here or in Provider + suspense: true // A. can put `suspense: true` here }, []) // onMount return todos.map(todo =>
    {todo.title}
    ) @@ -215,7 +219,7 @@ function Todos() { function App() { const options = { - suspense: true + suspense: true // B. can put `suspense: true` here too } return ( @@ -231,7 +235,7 @@ function App() {
    -
    Suspense Mode (managed state) +
    Suspense Mode (managed state) Can put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B). @@ -301,7 +305,7 @@ function App() {
    -
    Pagination + Provider +
    Pagination + Provider The `onNewData` will take the current data, and the newly fetched data, and allow you to merge the two however you choose. In the example below, we are appending the new todos to the end of the current todos. @@ -751,6 +755,46 @@ const App = () => {
    +
    Retries retryOn & retryDelay + +In this example you can see how `retryOn` will retry on a status code of `305`, or if we choose the `retryOn()` function, it returns a boolean to decide if we will retry. With `retryDelay` we can either have a fixed delay, or a dynamic one by using `retryDelay()`. Make sure `retries` is set to at minimum `1` otherwise it won't retry the request. If `retries > 0` without `retryOn` then by default we always retry if there's an error or if `!response.ok`. If `retryOn: [400]` and `retries > 0` then we only retry on a response status of `400`. + +```js +import useFetch from 'use-http' + +const TestRetry = () => { + const { response, get } = useFetch('https://httpbin.org/status/305', { + // make sure `retries` is set otherwise it won't retry + retries: 1, + retryOn: [305], + // OR + retryOn: ({ attempt, error, response }) => { + // returns true or false to determine whether to retry + return error || response && response.status >= 300 + }, + + retryDelay: 3000, + // OR + retryDelay: ({ attempt, error, response }) => { + // exponential backoff + return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000) + // linear backoff + return attempt * 1000 + } + }) + + return ( + <> + +
    {JSON.stringify(response, null, 2)}
    + + ) +} +``` + +[![Edit Basic Example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usefetch-retryon-retrydelay-s74q9) + +
    Overview -------- @@ -772,79 +816,108 @@ This is exactly what you would pass to the normal js `fetch`, with a little extr | Option | Description | Default | | --------------------- | --------------------------------------------------------------------------|------------- | -| `suspense` | Enables Experimental React Suspense mode. [example](https://codesandbox.io/s/usefetch-suspense-i22wv) | false | -| `cachePolicy` | These will be the same ones as Apollo's [fetch policies](https://www.apollographql.com/docs/react/api/react-apollo/#optionsfetchpolicy). Possible values are `cache-and-network`, `network-only`, `cache-only`, `no-cache`, `cache-first`. Currently only supports **`cache-first`** or **`no-cache`** | `cache-first` | | `cacheLife` | After a successful cache update, that cache data will become stale after this duration | `0` | -| `url` | Allows you to set a base path so relative paths can be used for each request :) | empty string | -| `onNewData` | Merges the current data with the incoming data. Great for pagination. | `(curr, new) => new` | -| `perPage` | Stops making more requests if there is no more data to fetch. (i.e. if we have 25 todos, and the perPage is 10, after fetching 2 times, we will have 20 todos. The last 5 tells us we don't have any more to fetch because it's less than 10) For pagination. | `0` | -| `onAbort` | Runs when the request is aborted. | empty function | -| `onTimeout` | Called when the request times out. | empty function | -| `retries` | When a request fails or times out, retry the request this many times. By default it will not retry. | `0` | -| `timeout` | The request will be aborted/cancelled after this amount of time. This is also the interval at which `retries` will be made at. **in milliseconds** | `30000`
    (30 seconds) | +| `cachePolicy` | These will be the same ones as Apollo's [fetch policies](https://www.apollographql.com/docs/react/api/react-apollo/#optionsfetchpolicy). Possible values are `cache-and-network`, `network-only`, `cache-only`, `no-cache`, `cache-first`. Currently only supports **`cache-first`** or **`no-cache`** | `cache-first` | | `data` | Allows you to set a default value for `data` | `undefined` | -| `loading` | Allows you to set default value for `loading` | `false` unless the last argument of `useFetch` is `[]` | | `interceptors.request` | Allows you to do something before an http request is sent out. Useful for authentication if you need to refresh tokens a lot. | `undefined` | | `interceptors.response` | Allows you to do something after an http response is recieved. Useful for something like camelCasing the keys of the response. | `undefined` | +| `loading` | Allows you to set default value for `loading` | `false` unless the last argument of `useFetch` is `[]` | +| `onAbort` | Runs when the request is aborted. | empty function | +| `onNewData` | Merges the current data with the incoming data. Great for pagination. | `(curr, new) => new` | +| `onTimeout` | Called when the request times out. | empty function | +| `path` | When using a global `url` set in the `Provider`, this is useful for adding onto it | `''` | | `persist` | Persists data for the duration of `cacheLife`. If `cacheLife` is not set it defaults to 24h. Currently only available in Browser. | `false` | +| `perPage` | Stops making more requests if there is no more data to fetch. (i.e. if we have 25 todos, and the perPage is 10, after fetching 2 times, we will have 20 todos. The last 5 tells us we don't have any more to fetch because it's less than 10) For pagination. | `0` | +| `retries` | When a request fails or times out, retry the request this many times. By default it will not retry. | `0` | +| `retryDelay` | You can retry with certain intervals i.e. 30 seconds `30000` or with custom logic (i.e. to increase retry intervals). | `1000` | +| `retryOn` | You can retry on certain http status codes or have custom logic to decide whether to retry or not via a function. Make sure `retries > 0` otherwise it won't retry. | `[]` | +| `suspense` | Enables Experimental React Suspense mode. [example](https://codesandbox.io/s/usefetch-suspense-i22wv) | `false` | +| `timeout` | The request will be aborted/cancelled after this amount of time. This is also the interval at which `retries` will be made at. **in milliseconds**. If set to `0`, it will not timeout except for browser defaults. | `0` | +| `url` | Allows you to set a base path so relative paths can be used for each request :) | empty string | ```jsx const options = { // accepts all `fetch` options such as headers, method, etc. - // enables experimental React Suspense mode - suspense: true, // defaults to `false` + // The time in milliseconds that cache data remains fresh. + cacheLife: 0, // Cache responses to improve speed and reduce amount of requests // Only one request to the same endpoint will be initiated unless cacheLife expires for 'cache-first'. cachePolicy: 'cache-first' // 'no-cache' + + // set's the default for the `data` field + data: [], - // The time in milliseconds that cache data remains fresh. - cacheLife: 0, - - // Allows caching to persist after page refresh. Only supported in the Browser currently. - persist: false, + // typically, `interceptors` would be added as an option to the `` + interceptors: { + request: async (options, url, path, route) => { // `async` is not required + return options // returning the `options` is important + }, + response: async (response) => { + // note: `response.data` is equivalent to `await response.json()` + return response // returning the `response` is important + } + }, - // used to be `baseUrl`. You can set your URL this way instead of as the 1st argument - url: 'https://example.com', - - // called when the request times out - onTimeout: () => {}, + // set's the default for `loading` field + loading: false, // called when aborting the request onAbort: () => {}, - // this will allow you to merge the data however you choose. Used for Pagination + // this will allow you to merge the `data` for pagination. onNewData: (currData, newData) => { return [...currData, ...newData] }, + // called when the request times out + onTimeout: () => {}, + + // if you have a global `url` set up, this is how you can add to it + path: '/path/to/your/api', + // this will tell useFetch not to run the request if the list doesn't haveMore. (pagination) // i.e. if the last page fetched was < 15, don't run the request again perPage: 15, + // Allows caching to persist after page refresh. Only supported in the Browser currently. + persist: false, + // amount of times it should retry before erroring out retries: 3, + + // The time between retries + retryDelay: 10000, + // OR + // Can be a function which is used if we want change the time in between each retry + retryDelay({ attempt, error, response }) { + // exponential backoff + return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000) + // linear backoff + return attempt * 1000 + }, + + // make sure `retries` is set otherwise it won't retry + // can retry on certain http status codes + retryOn: [503], + // OR + retryOn({ attempt, error, response }) { + // retry on any network error, or 4xx or 5xx status codes + if (error !== null || response.status >= 400) { + console.log(`retrying, attempt number ${attempt + 1}`); + return true; + } + }, + + // enables experimental React Suspense mode + suspense: true, // defaults to `false` - // amount of time before the request (or request(s) for each retry) errors out. + // amount of time before the request get's canceled/aborted timeout: 10000, - - // set's the default for the `data` field - data: [], - - // set's the default for `loading` field - loading: false, - - // typically, `interceptors` would be added as an option to the `` - interceptors: { - request: async (options, url, path, route) => { // `async` is not required - return options // returning the `options` is important - }, - response: async (response) => { - // note: `response.data` is equivalent to `await response.json()` - return response // returning the `response` is important - } - } + + // used to be `baseUrl`. You can set your URL this way instead of as the 1st argument + url: 'https://example.com', } useFetch(options) @@ -886,6 +959,11 @@ If you have feature requests, [submit an issue][1] to let us know what you would Todos ------ +- [ ] dynamically check content-type to get data. [Common mime types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) + - .arrayBuffer() [] + - .json() ['application/json'] + - .text() ['text/plain'] + - .blob() ['image/png', 'application/octet-stream'] - [ ] suspense - [ ] triggering it from outside the `` component. - add `.read()` to `request` @@ -909,35 +987,40 @@ Todos - [ ] the `onMount` works properly with all variants of passing `useEffect(fn, [request.get])` and not causing an infinite loop - [ ] `async` tests for `interceptors.response` - [ ] aborts fetch on unmount + - [ ] does not abort fetch on every rerender + - [ ] `retryDelay` and `timeout` are both set. It works, but is annoying to deal with timers in tests. [resource](https://github.com/fac-13/HP-game/issues/9) + - [ ] `timeout` with `retries > 0`. (also do `retires > 1`) Need to figure out how to advance timers properly to write this and the test above - [ ] take a look at how [react-apollo-hooks](https://github.com/trojanowski/react-apollo-hooks) work. Maybe ad `useSubscription` and `const request = useFetch(); request.subscribe()` or something along those lines - [ ] make this a github package - [see ava packages](https://github.com/orgs/ava/packages) -- [ ] get it all working on a SSR codesandbox, this way we can have api to call locally -- [ ] make GraphQL examples in codesandbox - [ ] Documentation: - [ ] show comparison with Apollo - [ ] figure out a good way to show side-by-side comparisons - [ ] show comparison with Axios - - [ ] how this cancels a request on unmount of a component to avoid the error "cannot update state during a state transition" or something like that due to an incomplete http request - [ ] maybe add syntax for middle helpers for inline `headers` or `queries` like this: -```jsx - const request = useFetch('https://example.com') - - request - .headers({ - auth: jwt // this would inline add the `auth` header - }) - .query({ // might have to use .params({ }) since we're using .query() for GraphQL - no: 'way' // this would inline make the url: https://example.com?no=way - }) - .get() -``` + ```jsx + const request = useFetch('https://example.com') + + request + .headers({ + auth: jwt // this would inline add the `auth` header + }) + .query({ // might have to use .params({ }) since we're using .query() for GraphQL + no: 'way' // this would inline make the url: https://example.com?no=way + }) + .get() + ``` - [ ] potential option ideas ```jsx const request = useFetch({ + graphql: { + // all options can also be put in here + // to overwrite those of `useFetch` for + // `useMutation` and `useQuery` + }, // Allows you to pass in your own cache to useFetch // This is controversial though because `cache` is an option in the requestInit // and it's value is a string. See: https://developer.mozilla.org/en-US/docs/Web/API/Request/cache @@ -951,23 +1034,6 @@ Todos request: async ({ options, url, path, route }) => {}, response: async ({ response }) => {} }, - // can retry on certain http status codes - retryOn: [503], - // OR - retryOn({ attempt, error, response }) { - // retry on any network error, or 4xx or 5xx status codes - if (error !== null || response.status >= 400) { - console.log(`retrying, attempt number ${attempt + 1}`); - return true; - } - }, - // This function receives a retryAttempt integer and returns the delay to apply before the next attempt in milliseconds - retryDelay({ attempt, error, response }) { - // applies exponential backoff - return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000) - // applies linear backoff - return attempt * 1000 - }, // these will be the exact same ones as Apollo's cachePolicy: 'cache-and-network', 'network-only', 'cache-only', 'no-cache' // 'cache-first' // potential idea to fetch on server instead of just having `loading` state. Not sure if this is a good idea though @@ -983,64 +1049,28 @@ Todos }) ``` -- resources - - [retryOn/retryDelay (fetch-retry)](https://www.npmjs.com/package/fetch-retry#example-retry-on-503-service-unavailable) - - [retryDelay (react-query)](https://github.com/tannerlinsley/react-query) - - [ ] potential option ideas for `GraphQL` -```jsx -const request = useQuery({ onMount: true })`your graphql query` - -const request = useFetch(...) -const userID = 'some-user-uuid' -const res = await request.query({ userID })` - query Todos($userID string!) { - todos(userID: $userID) { - id - title - } - } -` -``` - -- [ ] make code editor plugin/package/extension that adds GraphQL syntax highlighting for `useQuery` and `useMutation` 😊 + ```jsx + const request = useQuery({ onMount: true })`your graphql query` -
    GraphQL with Suspense (not implemented yet) - -```jsx -const App = () => { - const [todoTitle, setTodoTitle] = useState('') - // if there's no used, useMutation works this way - const mutation = useMutation('http://example.com', ` - mutation CreateTodo($todoTitle string) { - todo(title: $todoTitle) { + const request = useFetch(...) + const userID = 'some-user-uuid' + const res = await request.query({ userID })` + query Todos($userID string!) { + todos(userID: $userID) { id title } } - `) - - // ideally, I think it should be mutation.write({ todoTitle }) since mutation ~= POST - const createTodo = () => mutation.read({ todoTitle }) - - if (!request.data) return null - - return ( - <> - setTodoTitle(e.target.value)} /> - -
    {mutation.data}
    - - ) -} -``` -
    + ` + ``` +- [ ] make code editor plugin/package/extension that adds GraphQL syntax highlighting for `useQuery` and `useMutation` 😊 [1]: https://github.com/alex-cory/use-http/issues/new?title=[Feature%20Request]%20YOUR_FEATURE_NAME [2]: https://github.com/alex-cory/use-http/issues/93#issuecomment-600896722 [3]: https://github.com/alex-cory/use-http/raw/master/public/dog.png [4]: https://reactjs.org/docs/javascript-environment-requirements.html [5]: http://use-http.com -[`react-app-polyfill`]: https://www.npmjs.com/package/react-app-polyfill \ No newline at end of file +[`react-app-polyfill`]: https://www.npmjs.com/package/react-app-polyfill diff --git a/config/setupTests.ts b/config/setupTests.ts index 7187804c..870f39eb 100644 --- a/config/setupTests.ts +++ b/config/setupTests.ts @@ -3,19 +3,3 @@ import { GlobalWithFetchMock } from 'jest-fetch-mock' const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock customGlobal.fetch = require('jest-fetch-mock') customGlobal.fetchMock = customGlobal.fetch - -// this is just a little hack to silence a warning that we'll get until react -// fixes this: https://github.com/facebook/react/pull/14853 -const originalError = console.error -beforeAll(() => { - console.error = (...args: any[]) => { - if (/Warning.*not wrapped in act/.test(args[0])) { - return - } - originalError.call(console, ...args) - } -}) - -afterAll(() => { - console.error = originalError -}) diff --git a/docs/README.md b/docs/README.md index dab62717..2941d0a5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -61,6 +61,7 @@ Features - Built in caching - Persistent caching support - Suspense(experimental) support +- Retry functionality Examples ========= @@ -68,9 +69,10 @@ Examples - useFetch + Next.js - useFetch + create-react-app - useFetch + Provider --
  • useFetch + Suspense
  • +- useFetch + Suspense - useFetch + Pagination + Provider - useFetch + Request/Response Interceptors + Provider +- useFetch + retryOn, retryDelay - useQuery - GraphQL Installation @@ -574,6 +576,46 @@ const App = () => { } ``` +Retries +------- + +In this example you can see how `retryOn` will retry on a status code of `305`, or if we choose the `retryOn()` function, it returns a boolean to decide if we will retry. With `retryDelay` we can either have a fixed delay, or a dynamic one by using `retryDelay()`. Make sure `retries` is set to at minimum `1` otherwise it won't retry the request. If `retries > 0` without `retryOn` then by default we always retry if there's an error or if `!response.ok`. If `retryOn: [400]` and `retries > 0` then we only retry on a response status of `400`, not on any generic network error. + +```js +import useFetch from 'use-http' + +const TestRetry = () => { + const { response, get } = useFetch('https://httpbin.org/status/305', { + // make sure `retries` is set otherwise it won't retry + retries: 1, + retryOn: [305], + // OR + retryOn: ({ attempt, error, response }) => { + // returns true or false to determine whether to retry + return error || response && response.status >= 300 + }, + + retryDelay: 3000, + // OR + retryDelay: ({ attempt, error, response }) => { + // exponential backoff + return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000) + // linear backoff + return attempt * 1000 + } + }) + + return ( + <> + +
    {JSON.stringify(response, null, 2)}
    + + ) +} +``` + +[![Edit Basic Example](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/usefetch-retryon-retrydelay-s74q9) + GraphQL Query --------------- @@ -720,8 +762,8 @@ function App() { Hooks ======= -| Option | Description | -| --------------------- | ---------------------------------------------------------------------------------------- | +| Option | Description | +| --------------------- | ------------------ | | `useFetch` | The base hook | | `useQuery` | For making a GraphQL query | | `useMutation` | For making a GraphQL mutation | @@ -733,79 +775,109 @@ This is exactly what you would pass to the normal js `fetch`, with a little extr | Option | Description | Default | | --------------------- | --------------------------------------------------------------------------|------------- | -| `suspense` | Enables React Suspense mode. [example](https://codesandbox.io/s/usefetch-suspense-i22wv) | false | -| `cachePolicy` | These will be the same ones as Apollo's [fetch policies](https://www.apollographql.com/docs/react/api/react-apollo/#optionsfetchpolicy). Possible values are `cache-and-network`, `network-only`, `cache-only`, `no-cache`, `cache-first`. Currently only supports **`cache-first`** or **`no-cache`** | `cache-first` | | `cacheLife` | After a successful cache update, that cache data will become stale after this duration | `0` | -| `url` | Allows you to set a base path so relative paths can be used for each request :) | empty string | -| `onNewData` | Merges the current data with the incoming data. Great for pagination. | `(curr, new) => new` | -| `perPage` | Stops making more requests if there is no more data to fetch. (i.e. if we have 25 todos, and the perPage is 10, after fetching 2 times, we will have 20 todos. The last 5 tells us we don't have any more to fetch because it's less than 10) For pagination. | `0` | -| `onAbort` | Runs when the request is aborted. | empty function | -| `onTimeout` | Called when the request times out. | empty function | -| `retries` | When a request fails or times out, retry the request this many times. By default it will not retry. | `0` | -| `timeout` | The request will be aborted/cancelled after this amount of time. This is also the interval at which `retries` will be made at. **in milliseconds** | `30000`
    (30 seconds) | +| `cachePolicy` | These will be the same ones as Apollo's [fetch policies](https://www.apollographql.com/docs/react/api/react-apollo/#optionsfetchpolicy). Possible values are `cache-and-network`, `network-only`, `cache-only`, `no-cache`, `cache-first`. Currently only supports **`cache-first`** or **`no-cache`** | `cache-first` | | `data` | Allows you to set a default value for `data` | `undefined` | -| `loading` | Allows you to set default value for `loading` | `false` unless the last argument of `useFetch` is `[]` | | `interceptors.request` | Allows you to do something before an http request is sent out. Useful for authentication if you need to refresh tokens a lot. | `undefined` | | `interceptors.response` | Allows you to do something after an http response is recieved. Useful for something like camelCasing the keys of the response. | `undefined` | +| `loading` | Allows you to set default value for `loading` | `false` unless the last argument of `useFetch` is `[]` | +| `onAbort` | Runs when the request is aborted. | empty function | +| `onNewData` | Merges the current data with the incoming data. Great for pagination. | `(curr, new) => new` | +| `onTimeout` | Called when the request times out. | empty function | +| `path` | When using a global `url` set in the `Provider`, this is useful for adding onto it | `''` | | `persist` | Persists data for the duration of `cacheLife`. If `cacheLife` is not set it defaults to 24h. Currently only available in Browser. | `false` | +| `perPage` | Stops making more requests if there is no more data to fetch. (i.e. if we have 25 todos, and the perPage is 10, after fetching 2 times, we will have 20 todos. The last 5 tells us we don't have any more to fetch because it's less than 10) For pagination. | `0` | +| `retries` | When a request fails or times out, retry the request this many times. By default it will not retry. | `0` | +| `retryDelay` | You can retry with certain intervals i.e. 30 seconds `30000` or with custom logic (i.e. to increase retry intervals). | `1000` | +| `retryOn` | You can retry on certain http status codes or have custom logic to decide whether to retry or not via a function. Make sure `retries > 0` otherwise it won't retry. | `[]` | +| `suspense` | Enables Experimental React Suspense mode. [example](https://codesandbox.io/s/usefetch-suspense-i22wv) | `false` | +| `timeout` | The request will be aborted/cancelled after this amount of time. This is also the interval at which `retries` will be made at. **in milliseconds**. If set to `0`, it will not timeout except for browser defaults. | `0` | +| `url` | Allows you to set a base path so relative paths can be used for each request :) | empty string | ```jsx const options = { // accepts all `fetch` options such as headers, method, etc. - - // enables React Suspense mode - suspense: true, // defaults to `false` + + // The time in milliseconds that cache data remains fresh. + cacheLife: 0, // Cache responses to improve speed and reduce amount of requests // Only one request to the same endpoint will be initiated unless cacheLife expires for 'cache-first'. cachePolicy: 'cache-first' // 'no-cache' + + // set's the default for the `data` field + data: [], - // The time in milliseconds that cache data remains fresh. - cacheLife: 0, - - // Allows caching to persist after page refresh. Only supported in the Browser currently. - persist: false, + // typically, `interceptors` would be added as an option to the `` + interceptors: { + request: async (options, url, path, route) => { // `async` is not required + return options // returning the `options` is important + }, + response: async (response) => { + // note: `response.data` is equivalent to `await response.json()` + return response // returning the `response` is important + } + }, - // used to be `baseUrl`. You can set your URL this way instead of as the 1st argument - url: 'https://example.com', - - // called when the request times out - onTimeout: () => {}, + // set's the default for `loading` field + loading: false, // called when aborting the request onAbort: () => {}, - // this will allow you to merge the data however you choose. Used for Pagination + // this will allow you to merge the `data` for pagination. onNewData: (currData, newData) => { return [...currData, ...newData] }, + // called when the request times out + onTimeout: () => {}, + + // if you have a global `url` set up, this is how you can add to it + path: '/path/to/your/api', + // this will tell useFetch not to run the request if the list doesn't haveMore. (pagination) // i.e. if the last page fetched was < 15, don't run the request again perPage: 15, + // Allows caching to persist after page refresh. Only supported in the Browser currently. + persist: false, + // amount of times it should retry before erroring out retries: 3, + + // The time between retries + retryDelay: 10000, + // OR + // Can be a function which is used if we want change the time in between each retry + retryDelay({ attempt, error, response }) { + // exponential backoff + return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000) + // linear backoff + return attempt * 1000 + }, + + + // make sure `retries` is set otherwise it won't retry + // can retry on certain http status codes + retryOn: [503], + // OR + retryOn({ attempt, error, response }) { + // retry on any network error, or 4xx or 5xx status codes + if (error !== null || response.status >= 400) { + console.log(`retrying, attempt number ${attempt + 1}`); + return true; + } + }, + + // enables experimental React Suspense mode + suspense: true, // defaults to `false` - // amount of time before the request (or request(s) for each retry) errors out. + // amount of time before the request get's canceled/aborted timeout: 10000, - - // set's the default for the `data` field - data: [], - - // set's the default for `loading` field - loading: false, - - // typically, `interceptors` would be added as an option to the `` - interceptors: { - request: async (options, url, path, route) => { // `async` is not required - return options // returning the `options` is important - }, - response: async (response) => { // `async` is not required - // note: `response.data` is equivalent to `await response.json()` - return response // returning the `response` is important - } - } + + // used to be `baseUrl`. You can set your URL this way instead of as the 1st argument + url: 'https://example.com', } useFetch(options) @@ -883,4 +955,4 @@ const App = () => { [2]: https://github.com/alex-cory/use-http/issues/93#issuecomment-600896722 [3]: https://github.com/alex-cory/use-http/raw/master/public/dog.png [4]: https://reactjs.org/docs/javascript-environment-requirements.html -[`react-app-polyfill`]: https://www.npmjs.com/package/react-app-polyfill \ No newline at end of file +[`react-app-polyfill`]: https://www.npmjs.com/package/react-app-polyfill diff --git a/package.json b/package.json index e627a29c..f6045688 100644 --- a/package.json +++ b/package.json @@ -12,41 +12,42 @@ "use-ssr": "^1.0.22" }, "peerDependencies": { - "react": "^16.13.0", - "react-dom": "^16.13.0" + "react": "^16.13.1", + "react-dom": "^16.13.1" }, "devDependencies": { - "@testing-library/react": "^10.0.0", - "@testing-library/react-hooks": "^3.0.0", - "@types/fetch-mock": "^7.2.3", - "@types/jest": "^25.1.0", - "@types/node": "^13.9.0", - "@types/react": "^16.9.23", - "@types/react-dom": "^16.8.4", - "@typescript-eslint/eslint-plugin": "^2.23.0", - "@typescript-eslint/parser": "^2.23.0", + "@testing-library/react": "^10.0.2", + "@testing-library/react-hooks": "^3.2.1", + "@types/fetch-mock": "^7.3.2", + "@types/jest": "^25.1.4", + "@types/node": "^13.9.8", + "@types/react": "^16.9.30", + "@types/react-dom": "^16.9.5", + "@typescript-eslint/eslint-plugin": "^2.26.0", + "@typescript-eslint/parser": "^2.26.0", "convert-keys": "^1.3.4", "eslint": "^6.8.0", - "eslint-config-standard": "^14.1.0", - "eslint-plugin-import": "^2.20.1", - "eslint-plugin-jest": "23.8.2", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jest": "^23.8.2", "eslint-plugin-jest-formatting": "^1.2.0", - "eslint-plugin-jsx-a11y": "^6.2.1", - "eslint-plugin-node": "^11.0.0", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-react": "^7.19.0", - "eslint-plugin-react-hooks": "^2.5.0", + "eslint-plugin-react-hooks": "^3.0.0", "eslint-plugin-standard": "^4.0.1", "eslint-watch": "^6.0.1", - "jest": "^25.1.0", + "jest": "^25.2.4", "jest-fetch-mock": "^3.0.3", + "jest-mock-console": "^1.0.0", "mockdate": "^2.0.5", - "react": "^16.8.6", - "react-dom": "^16.8.6", + "react": "^16.13.1", + "react-dom": "^16.13.1", "react-hooks-testing-library": "^0.6.0", - "react-test-renderer": "^16.8.6", - "ts-jest": "^25.2.1", - "typescript": "^3.4.5", + "react-test-renderer": "^16.13.1", + "ts-jest": "^25.3.0", + "typescript": "^3.8.3", "utility-types": "^3.10.0", "watch": "^1.0.2" }, diff --git a/src/__tests__/useFetch.test.tsx b/src/__tests__/useFetch.test.tsx index 1572bed1..af2b2d76 100644 --- a/src/__tests__/useFetch.test.tsx +++ b/src/__tests__/useFetch.test.tsx @@ -1,54 +1,22 @@ /* eslint-disable no-var */ /* eslint-disable camelcase */ /* eslint-disable @typescript-eslint/camelcase */ -import React, { Suspense, ReactElement, ReactNode } from 'react' +import React, { ReactElement, ReactNode } from 'react' import { useFetch, Provider } from '..' import { cleanup } from '@testing-library/react' import { FetchMock } from 'jest-fetch-mock' -import { Res, Options, CachePolicies } from '../types' import { toCamel } from 'convert-keys' import { renderHook, act } from '@testing-library/react-hooks' -import { emptyCustomResponse } from '../utils' -import * as test from '@testing-library/react' -import ErrorBoundary from '../ErrorBoundary' - +import mockConsole from 'jest-mock-console' import * as mockdate from 'mockdate' +import { Res, Options, CachePolicies } from '../types' +import { emptyCustomResponse, sleep, makeError } from '../utils' + const fetch = global.fetch as FetchMock const { NO_CACHE, NETWORK_ONLY } = CachePolicies -// Provider Tests ================================================= -/** - * Test Cases - * Provider: - * 1. URL only - * 2. Options only - * 3. graphql only - * 4. URL and Options only - * 5. URL and graphql only - * 6. Options and graphql only - * 7. URL and graphql and Options - * useFetch: - * A. const [data, loading, error, request] = useFetch() - * B. const {data, loading, error, request} = useFetch() - * C. const [data, loading, error, request] = useFetch('http://url.com') - * D. const [data, loading, error, request] = useFetch('http://url.com', { onMount: true }) - * E. const [data, loading, error, request] = useFetch({ onMount: true }) - * F. const [data, loading, error, request] = useFetch({ url: 'http://url.com' }) - * G. const [data, loading, error, request] = useFetch(oldOptions => ({ ...newOptions })) - * H. const [data, loading, error, request] = useFetch('http://url.com', oldOptions => ({ ...newOptions })) - * Errors: - * SSR Tests: - */ - -/** - * Tests to add: - * - FormData - * - React Native - * - more `interceptor` tests. Specifically for the `data` that is not in the `response` object - */ - describe('useFetch - BROWSER - basic functionality', (): void => { const expected = { name: 'Alex Cory', @@ -66,9 +34,7 @@ describe('useFetch - BROWSER - basic functionality', (): void => { fetch.mockResponseOnce(JSON.stringify(expected)) }) - it('should execute GET command with object destructuring', async (): Promise< - void - > => { + it('should execute GET command with object destructuring', async (): Promise => { const { result, waitForNextUpdate } = renderHook( () => useFetch('https://example.com', []), // onMount === true { wrapper: wrapper as React.ComponentType } @@ -100,9 +66,7 @@ describe('useFetch - BROWSER - basic functionality', (): void => { expect(typeof result.current.mutate).toBe('function') }) - it('should execute GET command with arrray destructuring', async (): Promise< - void - > => { + it('should execute GET command with arrray destructuring', async (): Promise => { const { result, waitForNextUpdate } = renderHook( () => useFetch('sweet', []), // onMount === true { wrapper: wrapper as React.ComponentType } @@ -118,6 +82,21 @@ describe('useFetch - BROWSER - basic functionality', (): void => { expect(request.loading).toBe(false) expect(loading).toBe(false) }) + + it('should not be content-type: application/json by default if using FormData for request body', async (): Promise => { + const { result } = renderHook( + () => useFetch('url-1234567'), + { wrapper: wrapper as React.ComponentType } + ) + await act(async () => { + var formData = new FormData() + formData.append('username', 'AlexCory') + await result.current.post(formData) + const options = fetch.mock.calls[0][1] || {} + expect(options.method).toBe('POST') + expect(options.headers).toBeUndefined() + }) + }) }) describe('useFetch - BROWSER - with ', (): void => { @@ -139,9 +118,7 @@ describe('useFetch - BROWSER - with ', (): void => { fetch.mockResponseOnce(JSON.stringify(expected)) }) - it('should work correctly: useFetch({ onMount: true, data: [] })', async (): Promise< - void - > => { + it('should work correctly: useFetch({ onMount: true, data: [] })', async (): Promise => { const { result, waitForNextUpdate } = renderHook( () => useFetch({ data: {} }, []), // onMount === true { wrapper } @@ -154,9 +131,7 @@ describe('useFetch - BROWSER - with ', (): void => { expect(result.current.data).toMatchObject(expected) }) - it('should execute GET using Provider url', async (): Promise< - void - > => { + it('should execute GET using Provider url', async (): Promise => { const { result, waitForNextUpdate } = renderHook( () => useFetch({ data: {} }, []), // onMount === true { wrapper } @@ -168,29 +143,25 @@ describe('useFetch - BROWSER - with ', (): void => { expect(result.current.data).toMatchObject(expected) }) - it('should execute GET using Provider url: request = useFetch(), request.get()', async (): Promise< - void - > => { + it('should execute GET using Provider url: request = useFetch(), request.get()', async (): Promise => { const { result } = renderHook(() => useFetch(), { wrapper }) expect(result.current.loading).toBe(false) - await result.current.get() + await act(result.current.get) expect(result.current.loading).toBe(false) expect(result.current.data).toMatchObject(expected) }) - it('should execute GET using Provider url: request = useFetch(), request.get("/people")', async (): Promise< - void - > => { + it('should execute GET using Provider url: request = useFetch(), request.get("/people")', async (): Promise => { const { result } = renderHook(() => useFetch(), { wrapper }) expect(result.current.loading).toBe(false) - await result.current.get('/people') + await act(async () => { + await result.current.get('/people') + }) expect(result.current.loading).toBe(false) expect(result.current.data).toMatchObject(expected) }) - it('should merge the data onNewData for pagination', async (): Promise< - void - > => { + it('should merge the data onNewData for pagination', async (): Promise => { const { result, waitForNextUpdate } = renderHook( () => useFetch({ path: '/people', @@ -208,17 +179,12 @@ describe('useFetch - BROWSER - with ', (): void => { }) }) - it('should not make another request when there is no more data `perPage` pagination', async (): Promise< - void - > => { + it('should not make another request when there is no more data `perPage` pagination', async (): Promise => { fetch.resetMocks() const expected1 = [1, 2, 3] - fetch.mockResponse( - JSON.stringify(expected1) - ) - let page = 1 + fetch.mockResponse(JSON.stringify(expected1)) const { result, rerender, waitForNextUpdate } = renderHook( - () => useFetch(`https://example.com?page=${page}`, { + ({ page }) => useFetch(`https://example.com?page=${page}`, { data: [], perPage: 3, onNewData: (currData, newData) => { @@ -226,19 +192,22 @@ describe('useFetch - BROWSER - with ', (): void => { if (page === 2) return [...currData, 4] return [...currData, ...newData] } - }, [page]) // onMount === true + }, [page]), // onMount === true + { + initialProps: { page: 1 }, + wrapper + } ) expect(result.current.loading).toBe(true) await waitForNextUpdate() expect(result.current.loading).toBe(false) expect(result.current.data).toEqual(expected1) - page = 2 - rerender() + act(() => rerender({ page: 2 })) await waitForNextUpdate() expect(result.current.data).toEqual([...expected1, 4]) expect(fetch.mock.calls.length).toBe(2) - page = 3 - rerender() + act(() => rerender({ page: 3 })) + await waitForNextUpdate() expect(result.current.data).toEqual([...expected1, 4]) expect(fetch.mock.calls.length).toBe(2) }) @@ -280,9 +249,7 @@ describe('timeouts', (): void => { jest.useFakeTimers() }) - it(`should execute GET and timeout after ${timeout}ms, and fire 'onTimeout' and 'onAbort'`, async (): Promise< - void - > => { + it(`should execute GET and timeout after ${timeout}ms, and fire 'onTimeout' and 'onAbort'`, async (): Promise => { const onAbort = jest.fn() const onTimeout = jest.fn() const { result, waitForNextUpdate } = renderHook( @@ -309,14 +276,15 @@ describe('timeouts', (): void => { expect(onTimeout).toHaveBeenCalledTimes(1) }) - it(`should execute GET, fail after ${timeout}ms, then retry 1 additional time`, async (): Promise< - void - > => { + it(`should execute GET, fail after ${timeout}ms, then retry 1 additional time`, async (): Promise => { const onAbort = jest.fn() const onTimeout = jest.fn() const { result, waitForNextUpdate } = renderHook( () => useFetch({ retries: 1, + // TODO: this test times out if `retryDelay > 0` + // works in apps, not sure how to advance the timers correctly + retryDelay: 0, timeout, path: '/todos', onAbort, @@ -363,9 +331,11 @@ describe('caching - useFetch - BROWSER', (): void => { await waitForNextUpdate() expect(result.current.loading).toBe(false) expect(result.current.data).toEqual(expected) - // make a 2nd request - const responseData = await result.current.get() - expect(responseData).toEqual(expected) + await act(async () => { + // make a 2nd request + const responseData = await result.current.get() + expect(responseData).toEqual(expected) + }) expect(result.current.data).toEqual(expected) expect(fetch.mock.calls.length).toBe(1) expect(result.current.loading).toBe(false) @@ -378,14 +348,12 @@ describe('caching - useFetch - BROWSER', (): void => { await waitForNextUpdate() const { response } = result.current expect(result.current.loading).toBe(false) - let text - let json await act(async () => { - json = await result.current.get() - text = await response.text() + const json = await result.current.get() + const text = await response.text() + expect(text).toBe(JSON.stringify(expected)) + expect(json).toEqual(expected) }) - expect(text).toBe(JSON.stringify(expected)) - expect(json).toEqual(expected) }) it('should still have a `response` promise even when being cached. `cache-first` cachePolicy (array destructuring)', async (): Promise => { @@ -395,14 +363,12 @@ describe('caching - useFetch - BROWSER', (): void => { await waitForNextUpdate() var [, response] = result.current expect(result.current.loading).toBe(false) - let text - let json await act(async () => { - json = await result.current.get() - text = await response.text() + const json = await result.current.get() + const text = await response.text() + expect(text).toBe(JSON.stringify(expected)) + expect(json).toEqual(expected) }) - expect(text).toBe(JSON.stringify(expected)) - expect(json).toEqual(expected) }) it('should make a second request if cacheLife has exprired. `cache-first` cachePolicy', async (): Promise => { @@ -415,10 +381,10 @@ describe('caching - useFetch - BROWSER', (): void => { expect(result.current.loading).toBe(false) expect(result.current.data).toEqual(expected) expect(fetch.mock.calls.length).toEqual(1) - // wait ~20ms to allow cache to expire - await new Promise(resolve => setTimeout(resolve, 20)) - // make a 2nd request await act(async () => { + // wait ~20ms to allow cache to expire + await sleep(20) + // make a 2nd request await result.current.get() }) expect(result.current.data).toEqual(expected) @@ -451,8 +417,10 @@ describe('useFetch - BROWSER - with - Managed State', (): void => { { wrapper: wrapper as React.ComponentType } ) expect(result.current.loading).toBe(false) - const responseData = await result.current.post('/people', expected) - expect(responseData).toEqual(expected) + await act(async () => { + const responseData = await result.current.post('/people', expected) + expect(responseData).toEqual(expected) + }) expect(result.current.data).toEqual(expected) expect(result.current.loading).toBe(false) }) @@ -550,9 +518,7 @@ describe('useFetch - BROWSER - interceptors', (): void => { }) beforeEach((): void => { - fetch.mockResponseOnce( - JSON.stringify(snake_case) - ) + fetch.mockResponseOnce(JSON.stringify(snake_case)) }) it('should pass the proper response object for `interceptors.response`', async (): Promise => { @@ -560,19 +526,17 @@ describe('useFetch - BROWSER - interceptors', (): void => { () => useFetch(), { wrapper } ) - await result.current.get() + await act(result.current.get) expect(result.current.response.ok).toBe(true) expect(result.current.response.data).toEqual(expected) }) it('should have the `data` field correctly set when using a response interceptor', async (): Promise => { const { result } = renderHook( - () => useFetch('x'), + () => useFetch(), { wrapper } ) - await act(async () => { - await result.current.get() - }) + await act(result.current.get) expect(result.current.response.ok).toBe(true) expect(result.current.data).toEqual(expected) }) @@ -582,7 +546,7 @@ describe('useFetch - BROWSER - interceptors', (): void => { () => useFetch({ path: '/path' }), { wrapper } ) - await result.current.get() + await act(result.current.get) expect((fetch.mock.calls[0][1] as any).data).toEqual('path') }) @@ -591,7 +555,9 @@ describe('useFetch - BROWSER - interceptors', (): void => { () => useFetch(), { wrapper } ) - await result.current.get('/route') + await act(async () => { + await result.current.get('/route') + }) expect((fetch.mock.calls[0][1] as any).data).toEqual('route') }) @@ -600,7 +566,7 @@ describe('useFetch - BROWSER - interceptors', (): void => { () => useFetch('url'), { wrapper } ) - await result.current.get() + await act(result.current.get) expect((fetch.mock.calls[0][1] as any).data).toEqual('url') }) }) @@ -634,14 +600,16 @@ describe('useFetch - BROWSER - Overwrite Global Options set in Provider', (): vo () => useFetch(), { wrapper } ) - await result.current.get() - expect(fetch.mock.calls[0][0]).toBe('https://example.com') - expect((fetch.mock.calls[0][1] as any).headers).toEqual(expectedHeadersGET) - await result.current.post() - expect((fetch.mock.calls[1][1] as any).headers).toEqual(expectedHeadersPOSTandPUT) - await result.current.put() - expect((fetch.mock.calls[2][1] as any).headers).toEqual(expectedHeadersPOSTandPUT) - expect(fetch).toHaveBeenCalledTimes(3) + await act(async () => { + await result.current.get() + expect(fetch.mock.calls[0][0]).toBe('https://example.com') + expect((fetch.mock.calls[0][1] as any).headers).toEqual(expectedHeadersGET) + await result.current.post() + expect((fetch.mock.calls[1][1] as any).headers).toEqual(expectedHeadersPOSTandPUT) + await result.current.put() + expect((fetch.mock.calls[2][1] as any).headers).toEqual(expectedHeadersPOSTandPUT) + expect(fetch).toHaveBeenCalledTimes(3) + }) }) it('should have the correct headers set in the options set in the Provider', async (): Promise => { @@ -650,7 +618,7 @@ describe('useFetch - BROWSER - Overwrite Global Options set in Provider', (): vo () => useFetch(), { wrapper } ) - await result.current.get() + await act(result.current.get) expect(fetch.mock.calls[0][0]).toBe('https://example.com') expect((fetch.mock.calls[0][1] as any).headers).toEqual(expectedHeaders) expect(fetch).toHaveBeenCalledTimes(1) @@ -698,72 +666,238 @@ describe('useFetch - BROWSER - suspense', (): void => { afterEach((): void => { fetch.resetMocks() cleanup() - - test.cleanup() }) + const expected = 'yay suspense' beforeEach((): void => { - fetch.mockResponse(JSON.stringify('yay suspense')) + fetch.mockResponse(JSON.stringify(expected)) }) it('should render useFetch fallback', async () => { - function Section() { - const { data } = useFetch('https://a.co', { suspense: true }, []) - return
    {data}
    - } - const { container } = test.render( - fallback}> -
    - - ) + const { result, waitForNextUpdate } = renderHook(() => useFetch('https://a.co', { suspense: true }, [])) + await waitForNextUpdate() + expect(result.current.data).toBe(expected) + }) - expect(container.textContent).toMatchInlineSnapshot('"fallback"') - await test.act((): any => new Promise(resolve => setTimeout(resolve, 210))) - expect(container.textContent).toMatchInlineSnapshot('"yay suspense"') + it('should throw error', async () => { + fetch.resetMocks() + fetch.mockRejectOnce(makeError('Test', 'error')) + const { result, waitForNextUpdate } = renderHook(() => useFetch('url-test-111', { + suspense: true, + retryDelay: 0, + cachePolicy: NO_CACHE + }, [])) + await waitForNextUpdate() + expect(result.current.error.name).toBe('Test') + expect(result.current.error.message).toBe('error') }) +}) - it('should render multiple useFetch fallbacks', async () => { - function Section() { - const { data: d1 } = useFetch('https://a.co/1', { suspense: true }, []) - const { data: d2 } = useFetch('https://a.co/2', { suspense: true }, []) - return
    {d1} {d2}
    - } - const { container } = test.render( - fallback}> -
    - - ) +describe('useFetch - BROWSER - retryOn & retryDelay', (): void => { + describe('no network causing retry', () => { + // `retryOn` as an array I don't *think* needs to be tested here + const expectedError = makeError('TypeError', 'Failed to fetch') + + afterEach((): void => { + fetch.resetMocks() + cleanup() + }) + + beforeEach((): void => { + fetch.mockReject(expectedError) + }) - // TODO: I believe it should work with the commented out code below - expect(container.textContent).toMatchInlineSnapshot('" fallback"') - // await test.act((): any => new Promise(res => setTimeout(res, 10))) // still suspending - // expect(container.textContent).toMatchInlineSnapshot(`"fallback"`) - await test.act((): any => new Promise(resolve => setTimeout(resolve, 100))) // should recover - expect(container.textContent).toMatchInlineSnapshot('"yay suspense yay suspense"') + it('should retryOn custom function', async (): Promise => { + // should fail, then retry on error, fail again the retry 1 more time + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-2', { + retries: 2, + retryOn({ error }) { + return !!error + } + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(expectedError) + expect(fetch.mock.calls.length).toBe(3) + }) + + it('should retry with a `retryDelay` as a positive number', async (): Promise => { + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-5', { + retries: 2, + retryOn({ error }) { + return !!error + }, + retryDelay: 100 + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(expectedError) + expect(fetch.mock.calls.length).toBe(3) + }) + + it('should retry with a `retryDelay` as a function', async (): Promise => { + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-7', { + retries: 2, + retryOn({ error }) { + return !!error + }, + retryDelay() { + return 100 + } + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(expectedError) + expect(fetch.mock.calls.length).toBe(3) + }) }) - it('should throw errors', async () => { - function Section() { - const { data } = useFetch('https://a.co', { suspense: true }, []) - return
    {data}
    - } - // https://reactjs.org/docs/concurrent-mode-suspense.html#handling-errors - const { container } = test.render( - error boundary}> - fallback}> -
    - - - ) + // still `retryDelay` and `retryOn` + describe('has network but fails by something like a status code > 299', () => { + const expectedSuccess = { no: 'way' } + afterEach((): void => { + fetch.resetMocks() + cleanup() + }) + + beforeEach((): void => { + fetch.resetMocks() + fetch.mockResponseOnce('fail', { status: 401 }) + .mockResponseOnce('fail', { status: 400 }) + .mockResponseOnce(JSON.stringify(expectedSuccess)) + }) - expect(container.textContent).toMatchInlineSnapshot('"fallback"') - await test.act((): any => new Promise(resolve => setTimeout(resolve, 150))) // still suspending - expect(container.textContent).toMatchInlineSnapshot('"yay suspense"') + it('should retryOn specific error codes', async (): Promise => { + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url', { + retries: 2, + retryOn: [401] + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(makeError(400, 'Bad Request')) + expect(fetch.mock.calls.length).toBe(2) + }) + + it('should retryOn custom function', async (): Promise => { + // should fail, then retry on 401, fail again, but not retry on 400 + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-2', { + retries: 2, + retryOn({ response }) { + return !!(response && response.status === 401) + } + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(makeError(400, 'Bad Request')) + expect(fetch.mock.calls.length).toBe(2) + }) + + it('should retry 3 times, fail all 3, then retry 3 more times when called again', async (): Promise => { + fetch.resetMocks() + fetch.mockResponse('fail', { status: 400 }) + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-12', { + retries: 2, + retryOn({ response }) { + return !!(response && response.status === 400) + }, + cachePolicy: CachePolicies.NO_CACHE + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(makeError(400, 'Bad Request')) + expect(fetch.mock.calls.length).toBe(3) + await act(result.current.get) + expect(result.current.error).toEqual(makeError(400, 'Bad Request')) + expect(fetch.mock.calls.length).toBe(6) + }) + + it('should retry with a `retryDelay` as a positive number', async (): Promise => { + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-5', { + retries: 2, + retryOn: [401, 400], + retryDelay: 100 + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(undefined) + expect(result.current.data).toEqual(expectedSuccess) + expect(fetch.mock.calls.length).toBe(3) + }) + + it('should retry with a `retryDelay` as a function', async (): Promise => { + const { result, waitForNextUpdate } = renderHook( + () => useFetch('url-7', { + retries: 2, + retryOn: [401, 400], + retryDelay() { + return 100 + } + }, []) + ) + await waitForNextUpdate() + expect(result.current.error).toEqual(undefined) + expect(result.current.data).toEqual(expectedSuccess) + expect(fetch.mock.calls.length).toBe(3) + }) + + it('should error with a `retryDelay` that is not a postive # or a function returning a positive #', async (): Promise => { + fetch.resetMocks() + fetch.mockResponse('fail', { status: 400 }) + const { result } = renderHook(() => useFetch('some-url-122', { retryDelay: -1000, retryOn: [400], retries: 2 }, [])) + expect(result.error.name).toBe('Invariant Violation') + + // TODO: there is an issue with error testing some things + // see more detail here: https://github.com/testing-library/react-hooks-testing-library/issues/308 + // we basically want to test if when we call the `retry` function, if the `delay` is >= 0 + // Not catching invariant from the `retry` function + // let caughtError = null + // class ErrorBoundary extends React.Component { + // state = { hasError: false } + // componentDidCatch(error: any) { + // console.log('RUN') + // this.setState({ hasError: true }) + // // console.log('error', error) + // caughtError = error + // } + // render = () => !this.state.hasError && this.props.children + // } + // const wrapper = ({ children }: { children?: ReactNode }): ReactElement => {children} + // const { result } = renderHook(() => useFetch('Z', { retryDelay: -1000 }, []), { wrapper }) + const restoreConsole = mockConsole('error') + const { waitForNextUpdate } = renderHook(() => useFetch('some-url-12', { + retries: 2, + retryOn: [400], + retryDelay() { + return -1000 + } + }, [])) + await waitForNextUpdate() + expect(console.error).toHaveBeenCalledTimes(2) + restoreConsole() + }) + + it('should error if `retryOn` is not a function or an array of positive numbers', async (): Promise => { + // TODO: should we check to see if they are valid http status codes? + // - regex: /^[1-5][0-9][0-9]$/ + // - ts HttpStatusCodes enum: https://gist.github.com/RWOverdijk/6cef816cfdf5722228e01cc05fd4b094 + var { result } = renderHook(() => useFetch('url-11211', { retryOn: 1000 as any, retries: 2 }, [])) + expect(result.error.name).toBe('Invariant Violation') + // eslint-disable-next-line + var { result } = renderHook(() => useFetch('url-111211', { retryOn: ['c'] as any, retries: 2 }, [])) + expect(result.error.name).toBe('Invariant Violation') + }) }) }) describe('useFetch - BROWSER - errors', (): void => { - const expectedError = { name: 'error', message: 'error' } + const expectedError = makeError('error', 'error') const expectedSuccess = { name: 'Alex Cory' } afterEach((): void => { @@ -778,7 +912,7 @@ describe('useFetch - BROWSER - errors', (): void => { it('should set the `error` object when response.ok is false', async (): Promise => { fetch.resetMocks() - fetch.mockResponseOnce('fail', { + fetch.mockResponse('fail', { status: 401 }) const { result } = renderHook( @@ -790,45 +924,41 @@ describe('useFetch - BROWSER - errors', (): void => { ) expect(result.current.data).toEqual([]) expect(result.current.loading).toBe(false) - await result.current.get() - expect(result.current.error).toEqual({ - name: 401, - message: 'Unauthorized' - }) + await act(result.current.get) + expect(result.current.error).toEqual(makeError(401, 'Unauthorized')) expect(result.current.data).toEqual([]) }) it('should reset the error after each call', async (): Promise => { + fetch.resetMocks() + fetch.mockRejectOnce(expectedError) + fetch.mockResponseOnce(JSON.stringify(expectedSuccess)) const { result } = renderHook( - () => useFetch('https://example.com', { cachePolicy: NO_CACHE }) + () => useFetch('https://example.com/1', { cachePolicy: NO_CACHE, retries: 0 }) ) expect(result.current.loading).toBe(false) - await act(async () => { await result.current.get() - }) - - expect(result.current.error).toEqual(expectedError) - - await act(async () => { + expect(result.current.error).toEqual(expectedError) await result.current.get() + expect(result.current.error).toBe(undefined) + expect(result.current.data).toEqual(expectedSuccess) }) - - expect(result.current.error).toBe(undefined) - expect(result.current.data).toEqual(expectedSuccess) }) it('should leave the default `data` as array if response is undefined or error', async (): Promise => { + fetch.resetMocks() + fetch.mockReject(expectedError) const { result } = renderHook( () => useFetch({ - url: 'https://example.com', + url: 'https://example.com/2', data: [], cachePolicy: NO_CACHE }) ) expect(result.current.data).toEqual([]) expect(result.current.loading).toBe(false) - await result.current.get() + await act(result.current.get) expect(result.current.error).toEqual(expectedError) expect(result.current.data).toEqual([]) }) @@ -849,19 +979,21 @@ describe('useFetch - BROWSER - errors', (): void => { } it('should set the `error` properly for `interceptors.response`', async (): Promise => { + fetch.resetMocks() + fetch.mockReject(expectedError) const { result } = renderHook( () => useFetch(), { wrapper: wrapperCustomError } ) - await act(async () => { - await result.current.get() - }) + await act(result.current.get) expect(result.current.response.ok).toBe(undefined) expect(JSON.stringify(result.current.response)).toEqual(JSON.stringify(emptyCustomResponse)) expect(result.current.error).toEqual(expectedError) }) it('should set the `error` properly for `interceptors.response` onMount', async (): Promise => { + fetch.resetMocks() + fetch.mockReject(expectedError) const { result, waitForNextUpdate } = renderHook( () => useFetch('https://example.com', []), // onMount === true { wrapper: wrapperCustomError } @@ -903,9 +1035,7 @@ describe('useFetch - BROWSER - persistence', (): void => { const { waitForNextUpdate } = renderHook( () => useFetch({ url: 'https://persist.com', persist: true }, []) ) - await waitForNextUpdate() - expect(fetch).toHaveBeenCalledTimes(1) }) @@ -915,9 +1045,7 @@ describe('useFetch - BROWSER - persistence', (): void => { const { result, waitForNextUpdate } = renderHook( () => useFetch({ url: 'https://persist.com', persist: true }, []) ) - await waitForNextUpdate() - expect(fetch).toHaveBeenCalledTimes(0) expect(result.current.data).toEqual(expected) expect(result.current.response.ok).toBe(true) @@ -934,15 +1062,13 @@ describe('useFetch - BROWSER - persistence', (): void => { const { waitForNextUpdate } = renderHook( () => useFetch({ url: 'https://persist.com', persist: true }, []) ) - await waitForNextUpdate() - expect(fetch).toHaveBeenCalledTimes(1) }) it('should have `cache` in the return of useFetch', async (): Promise => { const { result } = renderHook( - () => useFetch({ url: 'https://persist.com', persist: true }, []) + () => useFetch({ url: 'https://persist.com', persist: true }) ) expect(result.current.cache).toBeDefined() expect(result.current.cache.get).toBeInstanceOf(Function) @@ -953,24 +1079,17 @@ describe('useFetch - BROWSER - persistence', (): void => { }) it('should error if passing wrong cachePolicy with persist: true', async (): Promise => { - try { - const { result } = renderHook( - () => useFetch({ url: 'https://persist.com', persist: true, cachePolicy: NO_CACHE }, []) - ) - expect(result.current.error).toBe(undefined) - } catch (err) { - expect(err.name).toBe('Invariant Violation') - expect(err.message).toBe('You cannot use option \'persist\' with cachePolicy: no-cache 🙅‍♂️') - } + var { result } = renderHook( + () => useFetch({ url: 'https://persist.com', persist: true, cachePolicy: NO_CACHE }, []) + ) + expect(result.error.name).toBe('Invariant Violation') + expect(result.error.message).toBe('You cannot use option \'persist\' with cachePolicy: no-cache 🙅‍♂️') - try { - const { result } = renderHook( - () => useFetch({ url: 'https://persist.com', persist: true, cachePolicy: NETWORK_ONLY }, []) - ) - expect(result.current.error).toBe(undefined) - } catch (err) { - expect(err.name).toBe('Invariant Violation') - expect(err.message).toBe('You cannot use option \'persist\' with cachePolicy: network-only 🙅‍♂️') - } + // eslint-disable-next-line + var { result } = renderHook( + () => useFetch({ url: 'https://persist.com', persist: true, cachePolicy: NETWORK_ONLY }, []) + ) + expect(result.error.name).toBe('Invariant Violation') + expect(result.error.message).toBe('You cannot use option \'persist\' with cachePolicy: network-only 🙅‍♂️') }) }) diff --git a/src/doFetchArgs.ts b/src/doFetchArgs.ts index 267bcff1..0999dcce 100644 --- a/src/doFetchArgs.ts +++ b/src/doFetchArgs.ts @@ -38,11 +38,12 @@ export default async function doFetchArgs( const body = ((): BodyInit | null => { if (isBodyObject(routeOrBody)) return JSON.stringify(routeOrBody) + if (routeOrBody instanceof FormData) return routeOrBody if ( !isServer && ((bodyAs2ndParam as any) instanceof FormData || (bodyAs2ndParam as any) instanceof URLSearchParams) - ) { return bodyAs2ndParam as string } + ) return bodyAs2ndParam as any if (isBodyObject(bodyAs2ndParam)) return JSON.stringify(bodyAs2ndParam) if (isBodyObject(initialOptions.body)) return JSON.stringify(initialOptions.body) return null @@ -50,7 +51,7 @@ export default async function doFetchArgs( const headers = ((): HeadersInit | null => { const contentType = ((initialOptions.headers || {}) as any)['Content-Type'] - const shouldAddContentType = !!contentType || [HTTPMethod.POST, HTTPMethod.PUT].includes(method) + const shouldAddContentType = !!contentType || [HTTPMethod.POST, HTTPMethod.PUT].includes(method) && !(body instanceof FormData) const headers: any = { ...initialOptions.headers } if (shouldAddContentType) { // default content types http://bit.ly/2N2ovOZ @@ -81,12 +82,11 @@ export default async function doFetchArgs( if (requestInterceptor) { const interceptor = await requestInterceptor(opts, initialURL, path, route) - return interceptor + return interceptor as any } return opts })() - // TODO: see if `Object.entries` is supported for IE // TODO: if the body is a file, and this is a large file, it might exceed the size // limit of the key size. Potential solution: base64 the body // used to tell if a request has already been made diff --git a/src/types.ts b/src/types.ts index c7f689cc..e96d7340 100644 --- a/src/types.ts +++ b/src/types.ts @@ -94,6 +94,10 @@ export type RouteAndBodyOnly = ( body: BodyInit | object, ) => Promise +export type RouteOrBody = string | BodyInit | object +export type Body = BodyInit | object +export type RetryOpts = { attempt: number, error?: Error, response?: Response } + export type NoArgs = () => Promise export type FetchData = ( @@ -169,21 +173,23 @@ export type Cache = { } export interface CustomOptions { - retries?: number - persist?: boolean - timeout?: number - path?: string - url?: string - loading?: boolean + cacheLife?: number + cachePolicy?: CachePolicies data?: any interceptors?: Interceptors + loading?: boolean onAbort?: () => void - onTimeout?: () => void onNewData?: (currData: any, newData: any) => any + onTimeout?: () => void + path?: string + persist?: boolean perPage?: number - cachePolicy?: CachePolicies - cacheLife?: number + retries?: number + retryOn?: RetryOn + retryDelay?: RetryDelay suspense?: boolean + timeout?: number + url?: string } export type Options = CustomOptions & @@ -197,6 +203,9 @@ export type OptionsMaybeURL = NoUrlOptions & // TODO: this is still yet to be implemented export type OverwriteGlobalOptions = (options: Options) => Options +export type RetryOn = (({ attempt, error, response }: { attempt: number, error: Error, response: Res | null }) => boolean) | number[] +export type RetryDelay = (({ attempt, error, response }: { attempt: number, error: Error, response: Res | null }) => number) | number + /** * Helpers */ diff --git a/src/useFetch.ts b/src/useFetch.ts index 195e2303..d3a237ef 100644 --- a/src/useFetch.ts +++ b/src/useFetch.ts @@ -1,4 +1,4 @@ -import { useEffect, useState, useCallback, useRef, useReducer } from 'react' +import { useEffect, useState, useCallback, useRef, useReducer, useMemo } from 'react' import useSSR from 'use-ssr' import { HTTPMethod, @@ -11,11 +11,14 @@ import { UseFetchArgs, CachePolicies, FetchData, - NoArgs + NoArgs, + RouteOrBody, + Body, + RetryOpts } from './types' import useFetchArgs from './useFetchArgs' import doFetchArgs from './doFetchArgs' -import { invariant, tryGetData, toResponseObject, useDeepCallback } from './utils' +import { invariant, tryGetData, toResponseObject, useDeepCallback, isFunction, sleep, makeError } from './utils' import useCache from './useCache' const { CACHE_FIRST } = CachePolicies @@ -24,19 +27,21 @@ const { CACHE_FIRST } = CachePolicies function useFetch(...args: UseFetchArgs): UseFetch { const { customOptions, requestInit, defaults, dependencies } = useFetchArgs(...args) const { - url: initialURL, - path, + cacheLife, + cachePolicy, // 'cache-first' by default interceptors, - persist, - timeout, - retries, - onTimeout, onAbort, onNewData, + onTimeout, + path, perPage, - cachePolicy, // 'cache-first' by default - cacheLife, - suspense + persist, + retries, + retryDelay, + retryOn, + suspense, + timeout, + url: initialURL, } = customOptions const cache = useCache({ persist, cacheLife, cachePolicy }) @@ -47,7 +52,7 @@ function useFetch(...args: UseFetchArgs): UseFetch { const res = useRef>({} as Res) const data = useRef(defaults.data) const timedout = useRef(false) - const attempts = useRef(retries) + const attempt = useRef(0) const error = useRef() const hasMore = useRef(true) const suspenseStatus = useRef('pending') @@ -58,9 +63,10 @@ function useFetch(...args: UseFetchArgs): UseFetch { const forceUpdate = useReducer(() => ({}), [])[1] const makeFetch = useDeepCallback((method: HTTPMethod): FetchData => { + const doFetch = async ( - routeOrBody?: string | BodyInit | object, - body?: BodyInit | object + routeOrBody?: RouteOrBody, + body?: Body ): Promise => { if (isServer) return // for now, we don't do anything on the server controller.current = new AbortController() @@ -80,7 +86,6 @@ function useFetch(...args: UseFetchArgs): UseFetch { interceptors.request ) - if (!suspense && mounted.current) setLoading(true) error.current = undefined if (response.isCached && cachePolicy === CACHE_FIRST) { @@ -88,18 +93,20 @@ function useFetch(...args: UseFetchArgs): UseFetch { res.current = response.cached as Res res.current.data = await tryGetData(response.cached, defaults.data) data.current = res.current.data as TData - if (!suspense && mounted.current) setLoading(false) + if (!suspense && mounted.current) forceUpdate() return data.current } catch (err) { error.current = err - if (mounted.current) setLoading(false) + if (mounted.current) forceUpdate() } } + if (!suspense && mounted.current) setLoading(true) + // don't perform the request if there is no more data to fetch (pagination) if (perPage > 0 && !hasMore.current && !error.current) return data.current - const timer = timeout > 0 && setTimeout(() => { + const timer = timeout && setTimeout(() => { timedout.current = true theController.abort() if (onTimeout) onTimeout() @@ -112,10 +119,6 @@ function useFetch(...args: UseFetchArgs): UseFetch { newRes = await fetch(url, options) res.current = newRes.clone() - if (cachePolicy === CACHE_FIRST) { - await cache.set(response.id, newRes.clone()) - } - newData = await tryGetData(newRes, defaults.data) res.current.data = onNewData(data.current, newData) @@ -123,24 +126,67 @@ function useFetch(...args: UseFetchArgs): UseFetch { invariant('data' in res.current, 'You must have `data` field on the Response returned from your `interceptors.response`') data.current = res.current.data as TData + const opts = { attempt: attempt.current, response: newRes } + const shouldRetry = ( + // if we just have `retries` set with NO `retryOn` then + // automatically retry on fail until attempts run out + !isFunction(retryOn) && Array.isArray(retryOn) && retryOn.length < 1 && newRes?.ok === false + // otherwise only retry when is specified + || Array.isArray(retryOn) && retryOn.includes(newRes.status) + || isFunction(retryOn) && (retryOn as Function)(opts) + ) && retries > 0 && retries > attempt.current + + if (shouldRetry) { + const data = await retry(opts, routeOrBody, body) + return data + } + + if (cachePolicy === CACHE_FIRST) { + await cache.set(response.id, newRes.clone()) + } + if (Array.isArray(data.current) && !!(data.current.length % perPage)) hasMore.current = false } catch (err) { - if (attempts.current > 0) return doFetch(routeOrBody, body) - if (attempts.current < 1 && timedout.current) error.current = { name: 'AbortError', message: 'Timeout Error' } - if (err.name !== 'AbortError') error.current = err + if (attempt.current >= retries && timedout.current) error.current = makeError('AbortError', 'Timeout Error') + const opts = { attempt: attempt.current, error: err } + const shouldRetry = ( + // if we just have `retries` set with NO `retryOn` then + // automatically retry on fail until attempts run out + !isFunction(retryOn) && Array.isArray(retryOn) && retryOn.length < 1 + // otherwise only retry when is specified + || isFunction(retryOn) && (retryOn as Function)(opts) + ) && retries > 0 && retries > attempt.current + + if (shouldRetry) { + const temp = await retry(opts, routeOrBody, body) + return temp + } + if (err.name !== 'AbortError') error.current = makeError(err.name, err.message) + } finally { - if (newRes && !newRes.ok && !error.current) error.current = { name: newRes.status, message: newRes.statusText } - if (attempts.current > 0) attempts.current -= 1 timedout.current = false if (timer) clearTimeout(timer) controller.current = undefined } + if (newRes && !newRes.ok && !error.current) error.current = makeError(newRes.status, newRes.statusText) if (!suspense && mounted.current) setLoading(false) + if (attempt.current === retries) attempt.current = 0 return data.current } // end of doFetch() + const retry = async (opts: RetryOpts, routeOrBody?: RouteOrBody, body?: Body) => { + const delay = (isFunction(retryDelay) ? (retryDelay as Function)(opts) : retryDelay) as number + if (!(Number.isInteger(delay) && delay >= 0)) { + console.error('retryDelay must be a number >= 0! If you\'re using it as a function, it must also return a number >= 0.') + } + attempt.current++ + if (delay) await sleep(delay) + const d = await doFetch(routeOrBody, body) + return d + } + if (suspense) { return async (...args) => { suspender.current = doFetch(...args).then( @@ -174,13 +220,13 @@ function useFetch(...args: UseFetchArgs): UseFetch { abort: () => controller.current && controller.current.abort(), query: (query, variables) => post({ query, variables }), mutate: (mutation, variables) => post({ mutation, variables }), - loading: loading, + loading, error: error.current, data: data.current, cache } - const response = toResponseObject(res, data) + const response = useMemo(() => toResponseObject(res, data), []) // onMount/onUpdate useEffect((): any => { diff --git a/src/useFetchArgs.ts b/src/useFetchArgs.ts index bae11f2a..47b36a4d 100644 --- a/src/useFetchArgs.ts +++ b/src/useFetchArgs.ts @@ -1,23 +1,25 @@ -import { OptionsMaybeURL, NoUrlOptions, Flatten, CachePolicies, Interceptors, OverwriteGlobalOptions, Options } from './types' -import { isString, isObject, invariant, pullOutRequestInit, isFunction } from './utils' +import { OptionsMaybeURL, NoUrlOptions, Flatten, CachePolicies, Interceptors, OverwriteGlobalOptions, Options, RetryOn, RetryDelay } from './types' +import { isString, isObject, invariant, pullOutRequestInit, isFunction, isPositiveNumber } from './utils' import { useContext, useMemo } from 'react' import FetchContext from './FetchContext' type UseFetchArgsReturn = { customOptions: { - retries: number - persist: boolean - timeout: number - path: string - url: string + cacheLife: number + cachePolicy: CachePolicies interceptors: Interceptors onAbort: () => void - onTimeout: () => void onNewData: (currData: any, newData: any) => any + onTimeout: () => void + path: string perPage: number - cachePolicy: CachePolicies - cacheLife: number + persist: boolean + retries: number + retryDelay: RetryDelay + retryOn: RetryOn | undefined suspense: boolean + timeout: number + url: string } requestInit: RequestInit defaults: { @@ -27,21 +29,23 @@ type UseFetchArgsReturn = { dependencies?: any[] } -export const useFetchArgsDefaults = { +export const useFetchArgsDefaults: UseFetchArgsReturn = { customOptions: { - retries: 0, - persist: false, - timeout: 30000, // 30 seconds - path: '', - url: '', + cacheLife: 0, + cachePolicy: CachePolicies.CACHE_FIRST, interceptors: {}, onAbort: () => { /* do nothing */ }, - onTimeout: () => { /* do nothing */ }, onNewData: (currData: any, newData: any) => newData, + onTimeout: () => { /* do nothing */ }, + path: '', perPage: 0, - cachePolicy: CachePolicies.CACHE_FIRST, - cacheLife: 0, - suspense: false + persist: false, + retries: 0, + retryDelay: 1000, + retryOn: [], + suspense: false, + timeout: 0, + url: '', }, requestInit: { headers: {} }, defaults: { @@ -51,7 +55,6 @@ export const useFetchArgsDefaults = { dependencies: undefined } -// TODO: see if `Object.entries` is supported for IE export const defaults = Object.entries(useFetchArgsDefaults).reduce((acc, [key, value]) => { if (isObject(value)) return { ...acc, ...value } return { ...acc, [key]: value } @@ -65,11 +68,11 @@ const useField = ( const context = useContext(FetchContext) const contextOptions = context.options || {} return useMemo((): DV => { - if (isObject(urlOrOptions) && urlOrOptions[field]) return urlOrOptions[field] - if (isObject(optionsNoURLs) && (optionsNoURLs as NoUrlOptions)[field as keyof NoUrlOptions]) { + if (isObject(urlOrOptions) && field in urlOrOptions) return urlOrOptions[field] + if (isObject(optionsNoURLs) && field in optionsNoURLs) { return (optionsNoURLs as NoUrlOptions)[field as keyof NoUrlOptions] } - if (contextOptions[field]) return contextOptions[field] + if (field in contextOptions) return contextOptions[field] return defaults[field] }, [urlOrOptions, field, optionsNoURLs, contextOptions]) } @@ -116,7 +119,6 @@ export default function useFetchArgs( const data = useField('data', urlOrOptions, optionsNoURLs) const path = useField('path', urlOrOptions, optionsNoURLs) const timeout = useField('timeout', urlOrOptions, optionsNoURLs) - const retries = useField('retries', urlOrOptions, optionsNoURLs) const persist = useField('persist', urlOrOptions, optionsNoURLs) const onAbort = useField<() => void>('onAbort', urlOrOptions, optionsNoURLs) const onTimeout = useField<() => void>('onTimeout', urlOrOptions, optionsNoURLs) @@ -124,7 +126,15 @@ export default function useFetchArgs( const perPage = useField('perPage', urlOrOptions, optionsNoURLs) const cachePolicy = useField('cachePolicy', urlOrOptions, optionsNoURLs) const cacheLife = useField('cacheLife', urlOrOptions, optionsNoURLs) + invariant(Number.isInteger(cacheLife) && cacheLife >= 0, '`cacheLife` must be a number >= 0') const suspense = useField('suspense', urlOrOptions, optionsNoURLs) + const retries = useField('retries', urlOrOptions, optionsNoURLs) + invariant(Number.isInteger(retries) && retries >= 0, '`retries` must be a number >= 0') + const retryOn = useField('retryOn', urlOrOptions, optionsNoURLs) + const isValidRetryOn = isFunction(retryOn) || (Array.isArray(retryOn) && retryOn.every(isPositiveNumber)) + invariant(isValidRetryOn, '`retryOn` must be an array of positive numbers or a function returning a boolean.') + const retryDelay = useField('retryDelay', urlOrOptions, optionsNoURLs) + invariant(isFunction(retryDelay) || Number.isInteger(retryDelay as number) && retryDelay >= 0, '`retryDelay` must be a positive number or a function returning a positive number.') const loading = useMemo((): boolean => { if (isObject(urlOrOptions)) return !!urlOrOptions.loading || Array.isArray(dependencies) @@ -181,7 +191,9 @@ export default function useFetchArgs( perPage, cachePolicy, cacheLife, - suspense + suspense, + retryOn, + retryDelay }, requestInit, defaults: { diff --git a/src/utils.ts b/src/utils.ts index 17624512..2ba89ea9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -83,6 +83,8 @@ export const isBodyObject = (obj: any): boolean => isObject(obj) || Array.isArra export const isFunction = (v: any): boolean => typeof v === 'function' +export const isNumber = (v: any): boolean => Object.prototype.toString.call(v) === '[object Number]' + // const requestFields = Object.getOwnPropertyNames(Object.getPrototypeOf(new Request(''))) // const responseFields = Object.getOwnPropertyNames(Object.getPrototypeOf(new Response())) // export const customResponseFields = [...responseFields, 'data'] @@ -203,6 +205,7 @@ export const toResponseObject = (res?: Response | MutableRefObject< export const emptyCustomResponse = toResponseObject() +// TODO: switch this to .reduce() const headersAsObject = (headers: Headers): object => { const obj: any = {} headers.forEach((value, key) => { @@ -213,8 +216,7 @@ const headersAsObject = (headers: Headers): object => { export const serializeResponse = async (response: Response) => { const body = await response.text() - const status = response.status - const statusText = response.statusText + const { status, statusText } = response const headers = headersAsObject(response.headers) return { body, @@ -231,3 +233,13 @@ function useDeepCompareMemoize(value: DependencyList) { } export const useDeepCallback = (cb: (method: HTTPMethod) => (...args: any) => any, deps: DependencyList) => useCallback(cb, useDeepCompareMemoize(deps)) + +export const sleep = (ms: number) => new Promise((resolve: any) => setTimeout(resolve, ms)) + +export const isPositiveNumber = (n: number) => Number.isInteger(n) && n > 0 + +export const makeError = (name: string | number, message: string) => { + const error = new Error(message) + error.name = name + '' + return error +} diff --git a/yarn.lock b/yarn.lock index 2cb6c654..4c2d4bcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,32 +10,33 @@ "@babel/highlight" "^7.8.3" "@babel/core@^7.1.0", "@babel/core@^7.7.5": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b" - integrity sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA== + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" + integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.7" - "@babel/helpers" "^7.8.4" - "@babel/parser" "^7.8.7" + "@babel/generator" "^7.9.0" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.0" + "@babel/parser" "^7.9.0" "@babel/template" "^7.8.6" - "@babel/traverse" "^7.8.6" - "@babel/types" "^7.8.7" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.1" - json5 "^2.1.0" + json5 "^2.1.2" lodash "^4.17.13" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.8.6", "@babel/generator@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.7.tgz#870b3cf7984f5297998152af625c4f3e341400f7" - integrity sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew== +"@babel/generator@^7.9.0": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.4.tgz#12441e90c3b3c4159cdecf312075bf1a8ce2dbce" + integrity sha512-rjP8ahaDy/ouhrvCoU1E5mqaitWrxwuNGU+dy1EpaoK48jZay4MdkskKGIMHLZNewg8sAsqpGSREJwP0zH3YQA== dependencies: - "@babel/types" "^7.8.7" + "@babel/types" "^7.9.0" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" @@ -56,11 +57,63 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + "@babel/helper-plugin-utils@^7.0.0", "@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" integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== +"@babel/helper-replace-supers@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" + integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.6" + "@babel/types" "^7.8.6" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + "@babel/helper-split-export-declaration@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" @@ -68,28 +121,33 @@ dependencies: "@babel/types" "^7.8.3" -"@babel/helpers@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73" - integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w== +"@babel/helper-validator-identifier@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed" + integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw== + +"@babel/helpers@^7.9.0": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" + integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== dependencies: "@babel/template" "^7.8.3" - "@babel/traverse" "^7.8.4" - "@babel/types" "^7.8.3" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" "@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" + integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== dependencies: + "@babel/helper-validator-identifier" "^7.9.0" chalk "^2.0.0" - esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.7.tgz#7b8facf95d25fef9534aad51c4ffecde1a61e26a" - integrity sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A== +"@babel/parser@^7.1.0", "@babel/parser@^7.7.5", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" + integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== "@babel/plugin-syntax-bigint@^7.0.0": version "7.8.3" @@ -106,17 +164,17 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/runtime-corejs3@^7.7.4", "@babel/runtime-corejs3@^7.8.3": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.7.tgz#8209d9dff2f33aa2616cb319c83fe159ffb07b8c" - integrity sha512-sc7A+H4I8kTd7S61dgB9RomXu/C+F4IrRr4Ytze4dnfx7AXEpCrejSNpjx7vq6y/Bak9S6Kbk65a/WgMLtg43Q== + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz#26fe4aa77e9f1ecef9b776559bbb8e84d34284b7" + integrity sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA== dependencies: core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.4", "@babel/runtime@^7.7.4", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" - integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== +"@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.4", "@babel/runtime@^7.7.4", "@babel/runtime@^7.9.2": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== dependencies: regenerator-runtime "^0.13.4" @@ -129,27 +187,27 @@ "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.4", "@babel/traverse@^7.8.6": - version "7.8.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" - integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== +"@babel/traverse@^7.1.0", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892" + integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w== dependencies: "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.6" + "@babel/generator" "^7.9.0" "@babel/helper-function-name" "^7.8.3" "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.6" - "@babel/types" "^7.8.6" + "@babel/parser" "^7.9.0" + "@babel/types" "^7.9.0" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7": - version "7.8.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d" - integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw== +"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5" + integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng== dependencies: - esutils "^2.0.2" + "@babel/helper-validator-identifier" "^7.9.0" lodash "^4.17.13" to-fast-properties "^2.0.0" @@ -181,81 +239,80 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.1.0.tgz#1fc765d44a1e11aec5029c08e798246bd37075ab" - integrity sha512-3P1DpqAMK/L07ag/Y9/Jup5iDEG9P4pRAuZiMQnU0JB3UOvCyYCjCoxr7sIA80SeyUCUKrr24fKAxVpmBgQonA== +"@jest/console@^25.2.3": + version "25.2.3" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.2.3.tgz#38ac19b916ff61457173799239472659e1a67c39" + integrity sha512-k+37B1aSvOt9tKHWbZZSOy1jdgzesB0bj96igCVUG1nAH1W5EoUfgc5EXbBVU08KSLvkVdWopLXaO3xfVGlxtQ== dependencies: - "@jest/source-map" "^25.1.0" + "@jest/source-map" "^25.2.1" chalk "^3.0.0" - jest-util "^25.1.0" + jest-util "^25.2.3" slash "^3.0.0" -"@jest/core@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.1.0.tgz#3d4634fc3348bb2d7532915d67781cdac0869e47" - integrity sha512-iz05+NmwCmZRzMXvMo6KFipW7nzhbpEawrKrkkdJzgytavPse0biEnCNr2wRlyCsp3SmKaEY+SGv7YWYQnIdig== +"@jest/core@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.2.4.tgz#382ef80369d3311f1df79db1ee19e958ae95cdad" + integrity sha512-WcWYShl0Bqfcb32oXtjwbiR78D/djhMdJW+ulp4/bmHgeODcsieqUJfUH+kEv8M7VNV77E6jds5aA+WuGh1nmg== dependencies: - "@jest/console" "^25.1.0" - "@jest/reporters" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.2.3" + "@jest/reporters" "^25.2.4" + "@jest/test-result" "^25.2.4" + "@jest/transform" "^25.2.4" + "@jest/types" "^25.2.3" ansi-escapes "^4.2.1" chalk "^3.0.0" exit "^0.1.2" graceful-fs "^4.2.3" - jest-changed-files "^25.1.0" - jest-config "^25.1.0" - jest-haste-map "^25.1.0" - jest-message-util "^25.1.0" - jest-regex-util "^25.1.0" - jest-resolve "^25.1.0" - jest-resolve-dependencies "^25.1.0" - jest-runner "^25.1.0" - jest-runtime "^25.1.0" - jest-snapshot "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" - jest-watcher "^25.1.0" + jest-changed-files "^25.2.3" + jest-config "^25.2.4" + jest-haste-map "^25.2.3" + jest-message-util "^25.2.4" + jest-regex-util "^25.2.1" + jest-resolve "^25.2.3" + jest-resolve-dependencies "^25.2.4" + jest-runner "^25.2.4" + jest-runtime "^25.2.4" + jest-snapshot "^25.2.4" + jest-util "^25.2.3" + jest-validate "^25.2.3" + jest-watcher "^25.2.4" micromatch "^4.0.2" p-each-series "^2.1.0" - realpath-native "^1.1.0" + realpath-native "^2.0.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.1.0.tgz#4a97f64770c9d075f5d2b662b5169207f0a3f787" - integrity sha512-cTpUtsjU4cum53VqBDlcW0E4KbQF03Cn0jckGPW/5rrE9tb+porD3+hhLtHAwhthsqfyF+bizyodTlsRA++sHg== +"@jest/environment@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.2.4.tgz#74f4d8dd87b427434d0b822cde37bc0e78f3e28b" + integrity sha512-wA4xlhD19/gukkDpJ5HQsTle0pgnzI5qMFEjw267lpTDC8d9N7Ihqr5pI+l0p8Qn1SQhai+glSqxrGdzKy4jxw== dependencies: - "@jest/fake-timers" "^25.1.0" - "@jest/types" "^25.1.0" - jest-mock "^25.1.0" + "@jest/fake-timers" "^25.2.4" + "@jest/types" "^25.2.3" + jest-mock "^25.2.3" -"@jest/fake-timers@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.1.0.tgz#a1e0eff51ffdbb13ee81f35b52e0c1c11a350ce8" - integrity sha512-Eu3dysBzSAO1lD7cylZd/CVKdZZ1/43SF35iYBNV1Lvvn2Undp3Grwsv8PrzvbLhqwRzDd4zxrY4gsiHc+wygQ== +"@jest/fake-timers@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.2.4.tgz#6821b6edde74fda2a42467ae92cc93095d4c9527" + integrity sha512-oC1TJiwfMcBttVN7Wz+VZnqEAgYTiEMu0QLOXpypR89nab0uCB31zm/QeBZddhSstn20qe3yqOXygp6OwvKT/Q== dependencies: - "@jest/types" "^25.1.0" - jest-message-util "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" + "@jest/types" "^25.2.3" + jest-message-util "^25.2.4" + jest-mock "^25.2.3" + jest-util "^25.2.3" lolex "^5.0.0" -"@jest/reporters@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.1.0.tgz#9178ecf136c48f125674ac328f82ddea46e482b0" - integrity sha512-ORLT7hq2acJQa8N+NKfs68ZtHFnJPxsGqmofxW7v7urVhzJvpKZG9M7FAcgh9Ee1ZbCteMrirHA3m5JfBtAaDg== +"@jest/reporters@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.2.4.tgz#aa01c20aab217150d3a6080d5c98ce0bf34b17ed" + integrity sha512-VHbLxM03jCc+bTLOluW/IqHR2G0Cl0iATwIQbuZtIUast8IXO4fD0oy4jpVGpG5b20S6REA8U3BaQoCW/CeVNQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^25.1.0" - "@jest/environment" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.2.3" + "@jest/test-result" "^25.2.4" + "@jest/transform" "^25.2.4" + "@jest/types" "^25.2.3" chalk "^3.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" @@ -265,11 +322,10 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.0" - jest-haste-map "^25.1.0" - jest-resolve "^25.1.0" - jest-runtime "^25.1.0" - jest-util "^25.1.0" - jest-worker "^25.1.0" + jest-haste-map "^25.2.3" + jest-resolve "^25.2.3" + jest-util "^25.2.3" + jest-worker "^25.2.1" slash "^3.0.0" source-map "^0.6.0" string-length "^3.1.0" @@ -278,71 +334,62 @@ optionalDependencies: node-notifier "^6.0.0" -"@jest/source-map@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.1.0.tgz#b012e6c469ccdbc379413f5c1b1ffb7ba7034fb0" - integrity sha512-ohf2iKT0xnLWcIUhL6U6QN+CwFWf9XnrM2a6ybL9NXxJjgYijjLSitkYHIdzkd8wFliH73qj/+epIpTiWjRtAA== +"@jest/source-map@^25.2.1": + version "25.2.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.2.1.tgz#b62ecf8ae76170b08eff8859b56eb7576df34ab8" + integrity sha512-PgScGJm1U27+9Te/cxP4oUFqJ2PX6NhBL2a6unQ7yafCgs8k02c0LSyjSIx/ao0AwcAdCczfAPDf5lJ7zoB/7A== dependencies: callsites "^3.0.0" graceful-fs "^4.2.3" source-map "^0.6.0" -"@jest/test-result@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.1.0.tgz#847af2972c1df9822a8200457e64be4ff62821f7" - integrity sha512-FZzSo36h++U93vNWZ0KgvlNuZ9pnDnztvaM7P/UcTx87aPDotG18bXifkf1Ji44B7k/eIatmMzkBapnAzjkJkg== +"@jest/test-result@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.2.4.tgz#8fc9eac58e82eb2a82e4058e68c3814f98f59cf5" + integrity sha512-AI7eUy+q2lVhFnaibDFg68NGkrxVWZdD6KBr9Hm6EvN0oAe7GxpEwEavgPfNHQjU2mi6g+NsFn/6QPgTUwM1qg== dependencies: - "@jest/console" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.2.3" + "@jest/transform" "^25.2.4" + "@jest/types" "^25.2.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.1.0.tgz#4df47208542f0065f356fcdb80026e3c042851ab" - integrity sha512-WgZLRgVr2b4l/7ED1J1RJQBOharxS11EFhmwDqknpknE0Pm87HLZVS2Asuuw+HQdfQvm2aXL2FvvBLxOD1D0iw== +"@jest/test-sequencer@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.2.4.tgz#28364aeddec140c696324114f63570f3de536c87" + integrity sha512-TEZm/Rkd6YgskdpTJdYLBtu6Gc11tfWPuSpatq0duH77ekjU8dpqX2zkPdY/ayuHxztV5LTJoV5BLtI9mZfXew== dependencies: - "@jest/test-result" "^25.1.0" - jest-haste-map "^25.1.0" - jest-runner "^25.1.0" - jest-runtime "^25.1.0" + "@jest/test-result" "^25.2.4" + jest-haste-map "^25.2.3" + jest-runner "^25.2.4" + jest-runtime "^25.2.4" -"@jest/transform@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.1.0.tgz#221f354f512b4628d88ce776d5b9e601028ea9da" - integrity sha512-4ktrQ2TPREVeM+KxB4zskAT84SnmG1vaz4S+51aTefyqn3zocZUnliLLm5Fsl85I3p/kFPN4CRp1RElIfXGegQ== +"@jest/transform@^25.2.4": + version "25.2.4" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.2.4.tgz#34336f37f13f62f7d1f5b93d5d150ba9eb3e11b9" + integrity sha512-6eRigvb+G6bs4kW5j1/y8wu4nCrmVuIe0epPBbiWaYlwawJ8yi1EIyK3d/btDqmBpN5GpN4YhR6iPPnDmkYdTA== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" babel-plugin-istanbul "^6.0.0" chalk "^3.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.3" - jest-haste-map "^25.1.0" - jest-regex-util "^25.1.0" - jest-util "^25.1.0" + jest-haste-map "^25.2.3" + jest-regex-util "^25.2.1" + jest-util "^25.2.3" micromatch "^4.0.2" pirates "^4.0.1" - realpath-native "^1.1.0" + realpath-native "^2.0.0" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" - integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^13.0.0" - -"@jest/types@^25.1.0": - version "25.1.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.1.0.tgz#b26831916f0d7c381e11dbb5e103a72aed1b4395" - integrity sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA== +"@jest/types@^25.2.3": + version "25.2.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.2.3.tgz#035c4fb94e2da472f359ff9a211915d59987f6b6" + integrity sha512-6oLQwO9mKif3Uph3RX5J1i3S7X7xtDHWBaaaoeKw8hOzV6YUd0qDcYcHZ6QXMHDIzSr7zzrEa51o2Ovlj6AtKQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^1.1.1" @@ -356,18 +403,18 @@ dependencies: type-detect "4.0.8" -"@testing-library/dom@^7.0.2": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.0.4.tgz#89909046b4a2818d423dd2c786faee4ddbe32838" - integrity sha512-+vrLcGDvopLPsBB7JgJhf8ZoOhBSeCsI44PKJL9YoKrP2AvCkqrTg+z77wEEZJ4tSNdxV0kymil7hSvsQQ7jMQ== +"@testing-library/dom@^7.1.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.2.0.tgz#948894c2ef52017d299c35da02e085498363cd1e" + integrity sha512-K1Sao38VxsTrjTkFkzeW8m/oCtgCI5lANCE7u9ZaF+TTL3uKuiZ+vazeurxjvRHAsE6PvXjOIl6JFuZfgcWJSQ== dependencies: - "@babel/runtime" "^7.8.4" - "@types/testing-library__dom" "^6.12.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__dom" "^7.0.0" aria-query "^4.0.2" - dom-accessibility-api "^0.3.0" + dom-accessibility-api "^0.4.2" pretty-format "^25.1.0" -"@testing-library/react-hooks@^3.0.0": +"@testing-library/react-hooks@^3.2.1": version "3.2.1" resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294" integrity sha512-1OB6Ksvlk6BCJA1xpj8/WWz0XVd1qRcgqdaFAq+xeC6l61Ucj0P6QpA5u+Db/x9gU4DCX8ziR5b66Mlfg0M2RA== @@ -384,14 +431,14 @@ "@types/react" "^16.8.22" "@types/react-test-renderer" "^16.8.2" -"@testing-library/react@^10.0.0": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.1.tgz#4f5e2a8836257c5bd3df640b21d7bea5a0d83ead" - integrity sha512-sMHWud2dcymOzq2AhEniICSijEwKeTiBX+K0y36FYNY7wH2t0SIP1o732Bf5dDY0jYoMC2hj2UJSVpZC/rDsWg== +"@testing-library/react@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.2.tgz#8eca7aa52d810cf7150048a2829fdc487162006d" + integrity sha512-YT6Mw0oJz7R6vlEkmo1FlUD+K15FeXApOB5Ffm9zooFVnrwkt00w18dUJFMOh1yRp9wTdVRonbor7o4PIpFCmA== dependencies: - "@babel/runtime" "^7.8.7" - "@testing-library/dom" "^7.0.2" - "@types/testing-library__react" "^9.1.3" + "@babel/runtime" "^7.9.2" + "@testing-library/dom" "^7.1.0" + "@types/testing-library__react" "^10.0.0" "@types/babel__core@^7.1.0": version "7.1.6" @@ -436,7 +483,7 @@ resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== -"@types/fetch-mock@^7.2.3": +"@types/fetch-mock@^7.3.2": version "7.3.2" resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.2.tgz#58805ba36a9357be92cc8c008dbfda937e9f7d8f" integrity sha512-NCEfv49jmDsBAixjMjEHKVgmVQlJ+uK56FOc+2roYPExnXCZDpi6mJOHQ3v23BiO84hBDStND9R2itJr7PNoow== @@ -461,7 +508,7 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@^25.1.0": +"@types/jest@^25.1.4": version "25.1.4" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.1.4.tgz#9e9f1e59dda86d3fd56afce71d1ea1b331f6f760" integrity sha512-QDDY2uNAhCV7TMCITrxz+MRk1EizcsevzfeS6LykIlq2V1E5oO4wXG8V2ZEd9w7Snxeeagk46YbMgZ8ESHx3sw== @@ -474,17 +521,22 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== -"@types/node@^13.9.0": - version "13.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.0.tgz#5b6ee7a77faacddd7de719017d0bc12f52f81589" - integrity sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ== +"@types/node@^13.9.8": + version "13.9.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.8.tgz#09976420fc80a7a00bf40680c63815ed8c7616f4" + integrity sha512-1WgO8hsyHynlx7nhP1kr0OFzsgKz5XDQL+Lfc3b1Q3qIln/n8cKD4m09NJ0+P1Rq7Zgnc7N0+SsMnoD1rEb0kA== + +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/react-dom@*", "@types/react-dom@^16.8.4": +"@types/react-dom@*", "@types/react-dom@^16.9.5": version "16.9.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7" integrity sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg== @@ -498,10 +550,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.8.22", "@types/react@^16.9.23": - version "16.9.23" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c" - integrity sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw== +"@types/react@*", "@types/react@^16.8.22", "@types/react@^16.9.30": + version "16.9.30" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.30.tgz#4750857769803c36afeb9ed30139bb77f9607223" + integrity sha512-nx+ixghmJkzcDYhtqvmf/jPcBvyH87yfLM+dUDH2n8lE356t18p2flj3mzkT/VO0N1wh6RCjtmWGFI3r/KhCxw== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -511,12 +563,12 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== -"@types/testing-library__dom@*", "@types/testing-library__dom@^6.12.1": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-6.14.0.tgz#1aede831cb4ed4a398448df5a2c54b54a365644e" - integrity sha512-sMl7OSv0AvMOqn1UJ6j1unPMIHRXen0Ita1ujnMX912rrOcawe4f7wu0Zt9GIQhBhJvH2BaibqFgQ3lP+Pj2hA== +"@types/testing-library__dom@*", "@types/testing-library__dom@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@types/testing-library__dom/-/testing-library__dom-7.0.0.tgz#c0fb7d1c2495a3d26f19342102142d47500f0319" + integrity sha512-1TEPWyqQ6IQ7R1hCegZmFSA3KrBQjdzJW7yC9ybpRcFst5XuPOqBGNr0mTAKbxwI/TrTyc1skeyLJrpcvAf93w== dependencies: - pretty-format "^24.3.0" + pretty-format "^25.1.0" "@types/testing-library__react-hooks@^3.0.0": version "3.2.0" @@ -526,10 +578,10 @@ "@types/react" "*" "@types/react-test-renderer" "*" -"@types/testing-library__react@^9.1.3": - version "9.1.3" - resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-9.1.3.tgz#35eca61cc6ea923543796f16034882a1603d7302" - integrity sha512-iCdNPKU3IsYwRK9JieSYAiX0+aYDXOGAmrC/3/M7AqqSDKnWWVv07X+Zk1uFSL7cMTUYzv4lQRfohucEocn5/w== +"@types/testing-library__react@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-10.0.0.tgz#8413a47f435bf7ce50bbc1b6d119300d39aed5bd" + integrity sha512-aByqRiRn9psCWbgW7a+gfW/LUQY/ChznnuPyWwLipcJm+rXaLNeYM4qL21jWPGn9W1H//oXgLE9aDlpkZSY3CQ== dependencies: "@types/react-dom" "*" "@types/testing-library__dom" "*" @@ -540,13 +592,6 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== -"@types/yargs@^13.0.0": - version "13.0.8" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.8.tgz#a38c22def2f1c2068f8971acb3ea734eb3c64a99" - integrity sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== - dependencies: - "@types/yargs-parser" "*" - "@types/yargs@^15.0.0": version "15.0.4" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" @@ -554,40 +599,40 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^2.23.0": - version "2.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.23.0.tgz#aa7133bfb7b685379d9eafe4ae9e08b9037e129d" - integrity sha512-8iA4FvRsz8qTjR0L/nK9RcRUN3QtIHQiOm69FzV7WS3SE+7P7DyGGwh3k4UNR2JBbk+Ej2Io+jLAaqKibNhmtw== +"@typescript-eslint/eslint-plugin@^2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz#04c96560c8981421e5a9caad8394192363cc423f" + integrity sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw== dependencies: - "@typescript-eslint/experimental-utils" "2.23.0" - eslint-utils "^1.4.3" + "@typescript-eslint/experimental-utils" "2.26.0" functional-red-black-tree "^1.0.1" regexpp "^3.0.0" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@2.23.0", "@typescript-eslint/experimental-utils@^2.5.0": - version "2.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.23.0.tgz#5d2261c8038ec1698ca4435a8da479c661dc9242" - integrity sha512-OswxY59RcXH3NNPmq+4Kis2CYZPurRU6mG5xPcn24CjFyfdVli5mySwZz/g/xDbJXgDsYqNGq7enV0IziWGXVQ== +"@typescript-eslint/experimental-utils@2.26.0", "@typescript-eslint/experimental-utils@^2.5.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz#063390c404d9980767d76274df386c0aa675d91d" + integrity sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.23.0" + "@typescript-eslint/typescript-estree" "2.26.0" eslint-scope "^5.0.0" + eslint-utils "^2.0.0" -"@typescript-eslint/parser@^2.23.0": - version "2.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.23.0.tgz#f3d4e2928ff647fe77fc2fcef1a3534fee6a3212" - integrity sha512-k61pn/Nepk43qa1oLMiyqApC6x5eP5ddPz6VUYXCAuXxbmRLqkPYzkFRKl42ltxzB2luvejlVncrEpflgQoSUg== +"@typescript-eslint/parser@^2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.26.0.tgz#385463615818b33acb72a25b39c03579df93d76f" + integrity sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ== dependencies: "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "2.23.0" - "@typescript-eslint/typescript-estree" "2.23.0" + "@typescript-eslint/experimental-utils" "2.26.0" + "@typescript-eslint/typescript-estree" "2.26.0" eslint-visitor-keys "^1.1.0" -"@typescript-eslint/typescript-estree@2.23.0": - version "2.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.23.0.tgz#d355960fab96bd550855488dcc34b9a4acac8d36" - integrity sha512-pmf7IlmvXdlEXvE/JWNNJpEvwBV59wtJqA8MLAxMKLXNKVRC3HZBXR/SlZLPWTCcwOSg9IM7GeRSV3SIerGVqw== +"@typescript-eslint/typescript-estree@2.26.0": + version "2.26.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz#d8132cf1ee8a72234f996519a47d8a9118b57d56" + integrity sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg== dependencies: debug "^4.1.1" eslint-visitor-keys "^1.1.0" @@ -647,7 +692,7 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" -ansi-regex@^4.0.0, ansi-regex@^4.1.0: +ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -805,16 +850,16 @@ axobject-query@^2.0.2: resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799" integrity sha512-ICt34ZmrVt8UQnvPl6TVyDTkmhXmAyAT4Jh5ugfGUX4MOrZ+U/ZY6/sdylRw3qGNr9Ub5AJsaHeDMzNLehRdOQ== -babel-jest@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.1.0.tgz#206093ac380a4b78c4404a05b3277391278f80fb" - integrity sha512-tz0VxUhhOE2y+g8R2oFrO/2VtVjA1lkJeavlhExuRBg3LdNJY9gwQ+Vcvqt9+cqy71MCTJhewvTB7Qtnnr9SWg== +babel-jest@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.2.4.tgz#b21b68d3af8f161c3e6e501e91f0dea8e652e344" + integrity sha512-+yDzlyJVWrqih9i2Cvjpt7COaN8vUwCsKGtxJLzg6I0xhxD54K8mvDUCliPKLufyzHh/c5C4MRj4Vk7VMjOjIg== dependencies: - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/transform" "^25.2.4" + "@jest/types" "^25.2.3" "@types/babel__core" "^7.1.0" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^25.1.0" + babel-preset-jest "^25.2.1" chalk "^3.0.0" slash "^3.0.0" @@ -829,21 +874,21 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.1.0.tgz#fb62d7b3b53eb36c97d1bc7fec2072f9bd115981" - integrity sha512-oIsopO41vW4YFZ9yNYoLQATnnN46lp+MZ6H4VvPKFkcc2/fkl3CfE/NZZSmnEIEsJRmJAgkVEK0R7Zbl50CpTw== +babel-plugin-jest-hoist@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.1.tgz#d0003a1f3d5caa281e1107fe03bbf16b799f9955" + integrity sha512-HysbCQfJhxLlyxDbKcB2ucGYV0LjqK4h6dBoI3RtFuOxTiTWK6XGZMsHb0tGh8iJdV4hC6Z2GCHzVvDeh9i0lQ== dependencies: "@types/babel__traverse" "^7.0.6" -babel-preset-jest@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.1.0.tgz#d0aebfebb2177a21cde710996fce8486d34f1d33" - integrity sha512-eCGn64olaqwUMaugXsTtGAM2I0QTahjEtnRu0ql8Ie+gDWAc1N6wqN0k2NilnyTunM69Pad7gJY7LOtwLimoFQ== +babel-preset-jest@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.2.1.tgz#4ccd0e577f69aa11b71806edfe8b25a5c3ac93a2" + integrity sha512-zXHJBM5iR8oEO4cvdF83AQqqJf3tJrXy3x8nfu2Nlqvn4cneg4Ca8M7cQvC5S9BzDDy1O0tZ9iXru9J6E3ym+A== dependencies: "@babel/plugin-syntax-bigint" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^25.1.0" + babel-plugin-jest-hoist "^25.2.1" balanced-match@^1.0.0: version "1.0.0" @@ -1199,9 +1244,9 @@ cssstyle@^2.0.0: cssom "~0.3.6" csstype@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" - integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== + version "2.6.10" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b" + integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w== damerau-levenshtein@^1.0.4: version "1.0.6" @@ -1253,6 +1298,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + 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" @@ -1292,10 +1342,10 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32" - integrity sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw== +diff-sequences@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.1.tgz#fcfe8aa07dd9b0c648396a478dabca8e76c6ab27" + integrity sha512-foe7dXnGlSh3jR1ovJmdv+77VQj98eKCHHwJPbZ2eEf0fHwKbkZicpPxEch9smZ+n2dnF6QFwkOQdLq9hpeJUg== doctrine@1.5.0: version "1.5.0" @@ -1319,10 +1369,10 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-accessibility-api@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.3.0.tgz#511e5993dd673b97c87ea47dba0e3892f7e0c983" - integrity sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA== +dom-accessibility-api@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.4.3.tgz#93ca9002eb222fd5a343b6e5e6b9cf5929411c4c" + integrity sha512-JZ8iPuEHDQzq6q0k7PKMGbrIdsgBB7TRrtVOUm4nSMCExlg5qQG4KXWTH2k90yggjM4tTumRGwTKJSldMzKyLA== domexception@^1.0.1: version "1.0.1" @@ -1363,10 +1413,10 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: - version "1.17.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" - integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" @@ -1406,10 +1456,10 @@ escodegen@^1.11.1: optionalDependencies: source-map "~0.6.1" -eslint-config-standard@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz#b23da2b76fe5a2eba668374f246454e7058f15d4" - integrity sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA== +eslint-config-standard@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea" + integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== eslint-import-resolver-node@^0.3.2: version "0.3.3" @@ -1420,9 +1470,9 @@ eslint-import-resolver-node@^0.3.2: resolve "^1.13.1" eslint-module-utils@^2.4.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz#7878f7504824e1b857dd2505b59a8e5eda26a708" - integrity sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q== + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== dependencies: debug "^2.6.9" pkg-dir "^2.0.0" @@ -1435,10 +1485,10 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" -eslint-plugin-import@^2.20.1: - version "2.20.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz#802423196dcb11d9ce8435a5fc02a6d3b46939b3" - integrity sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw== +eslint-plugin-import@^2.20.2: + version "2.20.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" + integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== dependencies: array-includes "^3.0.3" array.prototype.flat "^1.2.1" @@ -1458,14 +1508,14 @@ eslint-plugin-jest-formatting@^1.2.0: resolved "https://registry.yarnpkg.com/eslint-plugin-jest-formatting/-/eslint-plugin-jest-formatting-1.2.0.tgz#a9eef225520d53e63665d0f2fcf4d3eda894a18e" integrity sha512-EqsbDByAtdQa5vEhJFUFMqTW7fghN0Qhb8oulM7R3j9+9xRuMsQKCPjWvCIxpWhl3SJJmlxBC25o1pUXiBHaAw== -eslint-plugin-jest@23.8.2: +eslint-plugin-jest@^23.8.2: version "23.8.2" resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz#6f28b41c67ef635f803ebd9e168f6b73858eb8d4" integrity sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg== dependencies: "@typescript-eslint/experimental-utils" "^2.5.0" -eslint-plugin-jsx-a11y@^6.2.1: +eslint-plugin-jsx-a11y@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz#b872a09d5de51af70a97db1eea7dc933043708aa" integrity sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg== @@ -1480,10 +1530,10 @@ eslint-plugin-jsx-a11y@^6.2.1: has "^1.0.3" jsx-ast-utils "^2.2.1" -eslint-plugin-node@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.0.0.tgz#365944bb0804c5d1d501182a9bc41a0ffefed726" - integrity sha512-chUs/NVID+sknFiJzxoN9lM7uKSOEta8GC8365hw1nDfwIPIjjpRSwwPvQanWv8dt/pDe9EV4anmVSwdiSndNg== +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== dependencies: eslint-plugin-es "^3.0.0" eslint-utils "^2.0.0" @@ -1497,10 +1547,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@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-2.5.0.tgz#c50ab7ca5945ce6d1cf8248d9e185c80b54171b6" - integrity sha512-bzvdX47Jx847bgAYf0FPX3u1oxU+mKU8tqrpj4UX9A96SbAmj/HVEefEy6rJUog5u8QIlOPTKZcBpGn5kkKfAQ== +eslint-plugin-react-hooks@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz#9e80c71846eb68dd29c3b21d832728aa66e5bd35" + integrity sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw== eslint-plugin-react@^7.19.0: version "7.19.0" @@ -1628,11 +1678,11 @@ esprima@^4.0.0, esprima@^4.0.1: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.1.0.tgz#c5c0b66f383e7656404f86b31334d72524eddb48" - integrity sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q== + version "1.2.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.2.0.tgz#a010a519c0288f2530b3404124bfb5f02e9797fe" + integrity sha512-weltsSqdeWIX9G2qQZz7KlTRJdkkOCTPgLYJUz1Hacf48R4YOwGPHO3+ORfWedqJKbq5WQmsgK90n+pFLIKt/Q== dependencies: - estraverse "^4.0.0" + estraverse "^5.0.0" esrecurse@^4.1.0: version "4.2.1" @@ -1641,11 +1691,16 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.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" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.0.0.tgz#ac81750b482c11cca26e4b07e83ed8f75fbcdc22" + integrity sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -1725,17 +1780,17 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expect@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-25.1.0.tgz#7e8d7b06a53f7d66ec927278db3304254ee683ee" - integrity sha512-wqHzuoapQkhc3OKPlrpetsfueuEiMf3iWh0R8+duCu9PIjXoP7HgD5aeypwTnXUAjC8aMsiVDaWwlbJ1RlQ38g== +expect@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/expect/-/expect-25.2.4.tgz#b66e0777c861034ebc21730bb34e1839d5d46806" + integrity sha512-hfuPhPds4yOsZtIw4kwAg70r0hqGmpqekgA+VX7pf/3wZ6FY+xIOXZhNsPMMMsspYG/YIsbAiwqsdnD4Ht+bCA== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" ansi-styles "^4.0.0" - jest-get-type "^25.1.0" - jest-matcher-utils "^25.1.0" - jest-message-util "^25.1.0" - jest-regex-util "^25.1.0" + jest-get-type "^25.2.1" + jest-matcher-utils "^25.2.3" + jest-message-util "^25.2.4" + jest-regex-util "^25.2.1" extend-shallow@^2.0.1: version "2.0.1" @@ -1868,9 +1923,9 @@ flat-cache@^2.0.1: write "1.0.3" flatted@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" - integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== for-in@^1.0.2: version "1.0.2" @@ -1955,9 +2010,9 @@ getpass@^0.1.1: assert-plus "^1.0.0" glob-parent@^5.0.0, glob-parent@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" - integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== dependencies: is-glob "^4.0.1" @@ -2074,9 +2129,9 @@ html-encoding-sniffer@^1.0.2: whatwg-encoding "^1.0.1" html-escaper@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" - integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== http-signature@~1.2.0: version "1.2.0" @@ -2427,114 +2482,116 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.0.tgz#d4d16d035db99581b6194e119bbf36c963c5eb70" - integrity sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.1.tgz#1343217244ad637e0c3b18e7f6b746941a9b5e9a" + integrity sha512-Vm9xwCiQ8t2cNNnckyeAV0UdxKpcQUz4nMxsBvIu8n2kmPSiyb5uaF/8LpmKr+yqL/MdOXaX2Nmdo4Qyxium9Q== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jest-changed-files@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.1.0.tgz#73dae9a7d9949fdfa5c278438ce8f2ff3ec78131" - integrity sha512-bdL1aHjIVy3HaBO3eEQeemGttsq1BDlHgWcOjEOIAcga7OOEGWHD2WSu8HhL7I1F0mFFyci8VKU4tRNk+qtwDA== +jest-changed-files@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.2.3.tgz#ad19deef9e47ba37efb432d2c9a67dfd97cc78af" + integrity sha512-EFxy94dvvbqRB36ezIPLKJ4fDIC+jAdNs8i8uTwFpaXd6H3LVc3ova1lNS4ZPWk09OCR2vq5kSdSQgar7zMORg== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" execa "^3.2.0" throat "^5.0.0" -jest-cli@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.1.0.tgz#75f0b09cf6c4f39360906bf78d580be1048e4372" - integrity sha512-p+aOfczzzKdo3AsLJlhs8J5EW6ffVidfSZZxXedJ0mHPBOln1DccqFmGCoO8JWd4xRycfmwy1eoQkMsF8oekPg== +jest-cli@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.2.4.tgz#021c2383904696597abc060dcb133c82ebd8bfcc" + integrity sha512-zeY2pRDWKj2LZudIncvvguwLMEdcnJqc2jJbwza1beqi80qqLvkPF/BjbFkK2sIV3r+mfTJS+7ITrvK6pCdRjg== dependencies: - "@jest/core" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/core" "^25.2.4" + "@jest/test-result" "^25.2.4" + "@jest/types" "^25.2.3" chalk "^3.0.0" exit "^0.1.2" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" + jest-config "^25.2.4" + jest-util "^25.2.3" + jest-validate "^25.2.3" prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^15.0.0" + realpath-native "^2.0.0" + yargs "^15.3.1" -jest-config@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.1.0.tgz#d114e4778c045d3ef239452213b7ad3ec1cbea90" - integrity sha512-tLmsg4SZ5H7tuhBC5bOja0HEblM0coS3Wy5LTCb2C8ZV6eWLewHyK+3qSq9Bi29zmWQ7ojdCd3pxpx4l4d2uGw== +jest-config@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.2.4.tgz#f4f33238979f225683179c89d1e402893008975d" + integrity sha512-fxy3nIpwJqOUQJRVF/q+pNQb6dv5b9YufOeCbpPZJ/md1zXpiupbhfehpfODhnKOfqbzSiigtSLzlWWmbRxnqQ== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^25.1.0" - "@jest/types" "^25.1.0" - babel-jest "^25.1.0" + "@jest/test-sequencer" "^25.2.4" + "@jest/types" "^25.2.3" + babel-jest "^25.2.4" chalk "^3.0.0" + deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^25.1.0" - jest-environment-node "^25.1.0" - jest-get-type "^25.1.0" - jest-jasmine2 "^25.1.0" - jest-regex-util "^25.1.0" - jest-resolve "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" + jest-environment-jsdom "^25.2.4" + jest-environment-node "^25.2.4" + jest-get-type "^25.2.1" + jest-jasmine2 "^25.2.4" + jest-regex-util "^25.2.1" + jest-resolve "^25.2.3" + jest-util "^25.2.3" + jest-validate "^25.2.3" micromatch "^4.0.2" - pretty-format "^25.1.0" - realpath-native "^1.1.0" + pretty-format "^25.2.3" + realpath-native "^2.0.0" -jest-diff@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.1.0.tgz#58b827e63edea1bc80c1de952b80cec9ac50e1ad" - integrity sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw== +jest-diff@^25.1.0, jest-diff@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.2.3.tgz#54d601a0a754ef26e808a8c8dbadd278c215aa3f" + integrity sha512-VtZ6LAQtaQpFsmEzps15dQc5ELbJxy4L2DOSo2Ev411TUEtnJPkAMD7JneVypeMJQ1y3hgxN9Ao13n15FAnavg== dependencies: chalk "^3.0.0" - diff-sequences "^25.1.0" - jest-get-type "^25.1.0" - pretty-format "^25.1.0" + diff-sequences "^25.2.1" + jest-get-type "^25.2.1" + pretty-format "^25.2.3" -jest-docblock@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.1.0.tgz#0f44bea3d6ca6dfc38373d465b347c8818eccb64" - integrity sha512-370P/mh1wzoef6hUKiaMcsPtIapY25suP6JqM70V9RJvdKLrV4GaGbfUseUVk4FZJw4oTZ1qSCJNdrClKt5JQA== +jest-docblock@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.2.3.tgz#ac45280c43d59e7139f9fbe5896c6e0320c01ebb" + integrity sha512-d3/tmjLLrH5fpRGmIm3oFa3vOaD/IjPxtXVOrfujpfJ9y1tCDB1x/tvunmdOVAyF03/xeMwburl6ITbiQT1mVA== dependencies: detect-newline "^3.0.0" -jest-each@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.1.0.tgz#a6b260992bdf451c2d64a0ccbb3ac25e9b44c26a" - integrity sha512-R9EL8xWzoPySJ5wa0DXFTj7NrzKpRD40Jy+zQDp3Qr/2QmevJgkN9GqioCGtAJ2bW9P/MQRznQHQQhoeAyra7A== +jest-each@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.2.3.tgz#64067ba1508ebbd07e9b126c173ab371e8e6309d" + integrity sha512-RTlmCjsBDK2c9T5oO4MqccA3/5Y8BUtiEy7OOQik1iyCgdnNdHbI0pNEpyapZPBG0nlvZ4mIu7aY6zNUvLraAQ== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" chalk "^3.0.0" - jest-get-type "^25.1.0" - jest-util "^25.1.0" - pretty-format "^25.1.0" - -jest-environment-jsdom@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.1.0.tgz#6777ab8b3e90fd076801efd3bff8e98694ab43c3" - integrity sha512-ILb4wdrwPAOHX6W82GGDUiaXSSOE274ciuov0lztOIymTChKFtC02ddyicRRCdZlB5YSrv3vzr1Z5xjpEe1OHQ== - dependencies: - "@jest/environment" "^25.1.0" - "@jest/fake-timers" "^25.1.0" - "@jest/types" "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" - jsdom "^15.1.1" - -jest-environment-node@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.1.0.tgz#797bd89b378cf0bd794dc8e3dca6ef21126776db" - integrity sha512-U9kFWTtAPvhgYY5upnH9rq8qZkj6mYLup5l1caAjjx9uNnkLHN2xgZy5mo4SyLdmrh/EtB9UPpKFShvfQHD0Iw== - dependencies: - "@jest/environment" "^25.1.0" - "@jest/fake-timers" "^25.1.0" - "@jest/types" "^25.1.0" - jest-mock "^25.1.0" - jest-util "^25.1.0" + jest-get-type "^25.2.1" + jest-util "^25.2.3" + pretty-format "^25.2.3" + +jest-environment-jsdom@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.2.4.tgz#f2783541d0538b1bc43641703372cea6a2e83611" + integrity sha512-5dm+tNwrLmhELdjAwiQnVGf/U9iFMWdTL4/wyrMg2HU6RQnCiuxpWbIigLHUhuP1P2Ak0F4k3xhjrikboKyShA== + dependencies: + "@jest/environment" "^25.2.4" + "@jest/fake-timers" "^25.2.4" + "@jest/types" "^25.2.3" + jest-mock "^25.2.3" + jest-util "^25.2.3" + jsdom "^15.2.1" + +jest-environment-node@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.2.4.tgz#dc211dfb0d8b66dfc1965a8f846e72e54ff0c430" + integrity sha512-Jkc5Y8goyXPrLRHnrUlqC7P4o5zn2m4zw6qWoRJ59kxV1f2a5wK+TTGhrhCwnhW/Ckpdl/pm+LufdvhJkvJbiw== + dependencies: + "@jest/environment" "^25.2.4" + "@jest/fake-timers" "^25.2.4" + "@jest/types" "^25.2.3" + jest-mock "^25.2.3" + jest-util "^25.2.3" + semver "^6.3.0" jest-fetch-mock@^3.0.3: version "3.0.3" @@ -2544,251 +2601,259 @@ jest-fetch-mock@^3.0.3: cross-fetch "^3.0.4" promise-polyfill "^8.1.3" -jest-get-type@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876" - integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw== +jest-get-type@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.1.tgz#6c83de603c41b1627e6964da2f5454e6aa3c13a6" + integrity sha512-EYjTiqcDTCRJDcSNKbLTwn/LcDPEE7ITk8yRMNAOjEsN6yp+Uu+V1gx4djwnuj/DvWg0YGmqaBqPVGsPxlvE7w== -jest-haste-map@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.1.0.tgz#ae12163d284f19906260aa51fd405b5b2e5a4ad3" - integrity sha512-/2oYINIdnQZAqyWSn1GTku571aAfs8NxzSErGek65Iu5o8JYb+113bZysRMcC/pjE5v9w0Yz+ldbj9NxrFyPyw== +jest-haste-map@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.2.3.tgz#2649392b5af191f0167a27bfb62e5d96d7eaaade" + integrity sha512-pAP22OHtPr4qgZlJJFks2LLgoQUr4XtM1a+F5UaPIZNiCRnePA0hM3L7aiJ0gzwiNIYwMTfKRwG/S1L28J3A3A== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.3" - jest-serializer "^25.1.0" - jest-util "^25.1.0" - jest-worker "^25.1.0" + jest-serializer "^25.2.1" + jest-util "^25.2.3" + jest-worker "^25.2.1" micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" + which "^2.0.2" optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.1.0.tgz#681b59158a430f08d5d0c1cce4f01353e4b48137" - integrity sha512-GdncRq7jJ7sNIQ+dnXvpKO2MyP6j3naNK41DTTjEAhLEdpImaDA9zSAZwDhijjSF/D7cf4O5fdyUApGBZleaEg== +jest-jasmine2@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.2.4.tgz#5f77de83e1027f0c7588137055a80da773872374" + integrity sha512-juoKrmNmLwaheNbAg71SuUF9ovwUZCFNTpKVhvCXWk+SSeORcIUMptKdPCoLXV3D16htzhTSKmNxnxSk4SrTjA== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^25.1.0" - "@jest/source-map" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/environment" "^25.2.4" + "@jest/source-map" "^25.2.1" + "@jest/test-result" "^25.2.4" + "@jest/types" "^25.2.3" chalk "^3.0.0" co "^4.6.0" - expect "^25.1.0" + expect "^25.2.4" is-generator-fn "^2.0.0" - jest-each "^25.1.0" - jest-matcher-utils "^25.1.0" - jest-message-util "^25.1.0" - jest-runtime "^25.1.0" - jest-snapshot "^25.1.0" - jest-util "^25.1.0" - pretty-format "^25.1.0" + jest-each "^25.2.3" + jest-matcher-utils "^25.2.3" + jest-message-util "^25.2.4" + jest-runtime "^25.2.4" + jest-snapshot "^25.2.4" + jest-util "^25.2.3" + pretty-format "^25.2.3" throat "^5.0.0" -jest-leak-detector@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.1.0.tgz#ed6872d15aa1c72c0732d01bd073dacc7c38b5c6" - integrity sha512-3xRI264dnhGaMHRvkFyEKpDeaRzcEBhyNrOG5oT8xPxOyUAblIAQnpiR3QXu4wDor47MDTiHbiFcbypdLcLW5w== +jest-leak-detector@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.2.3.tgz#4cf39f137925e0061c04c24ca65cae36465f0238" + integrity sha512-yblCMPE7NJKl7778Cf/73yyFWAas5St0iiEBwq7RDyaz6Xd4WPFnPz2j7yDb/Qce71A1IbDoLADlcwD8zT74Aw== dependencies: - jest-get-type "^25.1.0" - pretty-format "^25.1.0" + jest-get-type "^25.2.1" + pretty-format "^25.2.3" -jest-matcher-utils@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220" - integrity sha512-KGOAFcSFbclXIFE7bS4C53iYobKI20ZWleAdAFun4W1Wz1Kkej8Ng6RRbhL8leaEvIOjGXhGf/a1JjO8bkxIWQ== +jest-matcher-utils@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.2.3.tgz#59285bd6d6c810debc9caa585ed985e46a3f28fd" + integrity sha512-ZmiXiwQRVM9MoKjGMP5YsGGk2Th5ncyRxfXKz5AKsmU8m43kgNZirckVzaP61MlSa9LKmXbevdYqVp1ZKAw2Rw== dependencies: chalk "^3.0.0" - jest-diff "^25.1.0" - jest-get-type "^25.1.0" - pretty-format "^25.1.0" + jest-diff "^25.2.3" + jest-get-type "^25.2.1" + pretty-format "^25.2.3" -jest-message-util@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.1.0.tgz#702a9a5cb05c144b9aa73f06e17faa219389845e" - integrity sha512-Nr/Iwar2COfN22aCqX0kCVbXgn8IBm9nWf4xwGr5Olv/KZh0CZ32RKgZWMVDXGdOahicM10/fgjdimGNX/ttCQ== +jest-message-util@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.2.4.tgz#b1441b9c82f5c11fc661303cbf200a2f136a7762" + integrity sha512-9wWMH3Bf+GVTv0GcQLmH/FRr0x0toptKw9TA8U5YFLVXx7Tq9pvcNzTyJrcTJ+wLqNbMPPJlJNft4MnlcrtF5Q== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/test-result" "^25.2.4" + "@jest/types" "^25.2.3" "@types/stack-utils" "^1.0.1" chalk "^3.0.0" micromatch "^4.0.2" slash "^3.0.0" stack-utils "^1.0.1" -jest-mock@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.1.0.tgz#411d549e1b326b7350b2e97303a64715c28615fd" - integrity sha512-28/u0sqS+42vIfcd1mlcg4ZVDmSUYuNvImP4X2lX5hRMLW+CN0BeiKVD4p+ujKKbSPKd3rg/zuhCF+QBLJ4vag== +jest-mock-console@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jest-mock-console/-/jest-mock-console-1.0.0.tgz#0ca2cbea3aa0af4893c8c5f3c2de45c3d4d15629" + integrity sha512-bN9UjH+Jd/5Gs+p9Xt9Mai4SoUQRFd3f+ZOSCjlcbuVRUhNvl1y9jvys6L7BUx+1Uz+3jOoaq1O+C6j3sSu7SQ== + +jest-mock@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.2.3.tgz#b37a581f59d61bd91db27a99bf7eb8b3e5e993d5" + integrity sha512-xlf+pyY0j47zoCs8zGGOGfWyxxLximE8YFOfEK8s4FruR8DtM/UjNj61um+iDuMAFEBDe1bhCXkqiKoCmWjJzg== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" jest-pnp-resolver@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== -jest-regex-util@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.1.0.tgz#efaf75914267741838e01de24da07b2192d16d87" - integrity sha512-9lShaDmDpqwg+xAd73zHydKrBbbrIi08Kk9YryBEBybQFg/lBWR/2BDjjiSE7KIppM9C5+c03XiDaZ+m4Pgs1w== +jest-regex-util@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.1.tgz#db64b0d15cd3642c93b7b9627801d7c518600584" + integrity sha512-wroFVJw62LdqTdkL508ZLV82FrJJWVJMIuYG7q4Uunl1WAPTf4ftPKrqqfec4SvOIlvRZUdEX2TFpWR356YG/w== -jest-resolve-dependencies@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.1.0.tgz#8a1789ec64eb6aaa77fd579a1066a783437e70d2" - integrity sha512-Cu/Je38GSsccNy4I2vL12ZnBlD170x2Oh1devzuM9TLH5rrnLW1x51lN8kpZLYTvzx9j+77Y5pqBaTqfdzVzrw== +jest-resolve-dependencies@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.2.4.tgz#2d904400387d74a366dff54badb40a2b3210e733" + integrity sha512-qhUnK4PfNHzNdca7Ub1mbAqE0j5WNyMTwxBZZJjQlUrdqsiYho/QGK65FuBkZuSoYtKIIqriR9TpGrPEc3P5Gg== dependencies: - "@jest/types" "^25.1.0" - jest-regex-util "^25.1.0" - jest-snapshot "^25.1.0" + "@jest/types" "^25.2.3" + jest-regex-util "^25.2.1" + jest-snapshot "^25.2.4" -jest-resolve@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.1.0.tgz#23d8b6a4892362baf2662877c66aa241fa2eaea3" - integrity sha512-XkBQaU1SRCHj2Evz2Lu4Czs+uIgJXWypfO57L7JYccmAXv4slXA6hzNblmcRmf7P3cQ1mE7fL3ABV6jAwk4foQ== +jest-resolve@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.2.3.tgz#ababeaf2bb948cb6d2dea8453759116da0fb7842" + integrity sha512-1vZMsvM/DBH258PnpUNSXIgtzpYz+vCVCj9+fcy4akZl4oKbD+9hZSlfe9RIDpU0Fc28ozHQrmwX3EqFRRIHGg== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" browser-resolve "^1.11.3" chalk "^3.0.0" jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" + realpath-native "^2.0.0" + resolve "^1.15.1" -jest-runner@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.1.0.tgz#fef433a4d42c89ab0a6b6b268e4a4fbe6b26e812" - integrity sha512-su3O5fy0ehwgt+e8Wy7A8CaxxAOCMzL4gUBftSs0Ip32S0epxyZPDov9Znvkl1nhVOJNf4UwAsnqfc3plfQH9w== +jest-runner@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.2.4.tgz#d0daf7c56b4a83b6b675863d5cdcd502c960f9a1" + integrity sha512-5xaIfqqxck9Wg2CV4b9KmJtf/sWO7zWQx7O+34GCLGPzoPcVmB3mZtdrQI1/jS3Reqjru9ycLjgLHSf6XoxRqA== dependencies: - "@jest/console" "^25.1.0" - "@jest/environment" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/console" "^25.2.3" + "@jest/environment" "^25.2.4" + "@jest/test-result" "^25.2.4" + "@jest/types" "^25.2.3" chalk "^3.0.0" exit "^0.1.2" graceful-fs "^4.2.3" - jest-config "^25.1.0" - jest-docblock "^25.1.0" - jest-haste-map "^25.1.0" - jest-jasmine2 "^25.1.0" - jest-leak-detector "^25.1.0" - jest-message-util "^25.1.0" - jest-resolve "^25.1.0" - jest-runtime "^25.1.0" - jest-util "^25.1.0" - jest-worker "^25.1.0" + jest-config "^25.2.4" + jest-docblock "^25.2.3" + jest-haste-map "^25.2.3" + jest-jasmine2 "^25.2.4" + jest-leak-detector "^25.2.3" + jest-message-util "^25.2.4" + jest-resolve "^25.2.3" + jest-runtime "^25.2.4" + jest-util "^25.2.3" + jest-worker "^25.2.1" source-map-support "^0.5.6" throat "^5.0.0" -jest-runtime@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.1.0.tgz#02683218f2f95aad0f2ec1c9cdb28c1dc0ec0314" - integrity sha512-mpPYYEdbExKBIBB16ryF6FLZTc1Rbk9Nx0ryIpIMiDDkOeGa0jQOKVI/QeGvVGlunKKm62ywcioeFVzIbK03bA== - dependencies: - "@jest/console" "^25.1.0" - "@jest/environment" "^25.1.0" - "@jest/source-map" "^25.1.0" - "@jest/test-result" "^25.1.0" - "@jest/transform" "^25.1.0" - "@jest/types" "^25.1.0" +jest-runtime@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.2.4.tgz#c66a421e115944426b377a7fd331f6c0902cfa56" + integrity sha512-6ehOUizgIghN+aV5YSrDzTZ+zJ9omgEjJbTHj3Jqes5D52XHfhzT7cSfdREwkNjRytrR7mNwZ7pRauoyNLyJ8Q== + dependencies: + "@jest/console" "^25.2.3" + "@jest/environment" "^25.2.4" + "@jest/source-map" "^25.2.1" + "@jest/test-result" "^25.2.4" + "@jest/transform" "^25.2.4" + "@jest/types" "^25.2.3" "@types/yargs" "^15.0.0" chalk "^3.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.3" - jest-config "^25.1.0" - jest-haste-map "^25.1.0" - jest-message-util "^25.1.0" - jest-mock "^25.1.0" - jest-regex-util "^25.1.0" - jest-resolve "^25.1.0" - jest-snapshot "^25.1.0" - jest-util "^25.1.0" - jest-validate "^25.1.0" - realpath-native "^1.1.0" + jest-config "^25.2.4" + jest-haste-map "^25.2.3" + jest-message-util "^25.2.4" + jest-mock "^25.2.3" + jest-regex-util "^25.2.1" + jest-resolve "^25.2.3" + jest-snapshot "^25.2.4" + jest-util "^25.2.3" + jest-validate "^25.2.3" + realpath-native "^2.0.0" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.0.0" + yargs "^15.3.1" -jest-serializer@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.1.0.tgz#73096ba90e07d19dec4a0c1dd89c355e2f129e5d" - integrity sha512-20Wkq5j7o84kssBwvyuJ7Xhn7hdPeTXndnwIblKDR2/sy1SUm6rWWiG9kSCgJPIfkDScJCIsTtOKdlzfIHOfKA== +jest-serializer@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.2.1.tgz#51727a5fc04256f461abe0fa024a022ba165877a" + integrity sha512-fibDi7M5ffx6c/P66IkvR4FKkjG5ldePAK1WlbNoaU4GZmIAkS9Le/frAwRUFEX0KdnisSPWf+b1RC5jU7EYJQ== -jest-snapshot@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.1.0.tgz#d5880bd4b31faea100454608e15f8d77b9d221d9" - integrity sha512-xZ73dFYN8b/+X2hKLXz4VpBZGIAn7muD/DAg+pXtDzDGw3iIV10jM7WiHqhCcpDZfGiKEj7/2HXAEPtHTj0P2A== +jest-snapshot@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.2.4.tgz#08d4517579c864df4280bcc948ceea34327a4ded" + integrity sha512-nIwpW7FZCq5p0AE3Oyqyb6jL0ENJixXzJ5/CD/XRuOqp3gS5OM3O/k+NnTrniCXxPFV4ry6s9HNfiPQBi0wcoA== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" + "@types/prettier" "^1.19.0" chalk "^3.0.0" - expect "^25.1.0" - jest-diff "^25.1.0" - jest-get-type "^25.1.0" - jest-matcher-utils "^25.1.0" - jest-message-util "^25.1.0" - jest-resolve "^25.1.0" - mkdirp "^0.5.1" + expect "^25.2.4" + jest-diff "^25.2.3" + jest-get-type "^25.2.1" + jest-matcher-utils "^25.2.3" + jest-message-util "^25.2.4" + jest-resolve "^25.2.3" + make-dir "^3.0.0" natural-compare "^1.4.0" - pretty-format "^25.1.0" - semver "^7.1.1" + pretty-format "^25.2.3" + semver "^6.3.0" -jest-util@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.1.0.tgz#7bc56f7b2abd534910e9fa252692f50624c897d9" - integrity sha512-7did6pLQ++87Qsj26Fs/TIwZMUFBXQ+4XXSodRNy3luch2DnRXsSnmpVtxxQ0Yd6WTipGpbhh2IFP1mq6/fQGw== +jest-util@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.2.3.tgz#0abf95a1d6b96f2de5a3ecd61b36c40a182dc256" + integrity sha512-7tWiMICVSo9lNoObFtqLt9Ezt5exdFlWs5fLe1G4XLY2lEbZc814cw9t4YHScqBkWMfzth8ASHKlYBxiX2rdCw== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" chalk "^3.0.0" is-ci "^2.0.0" - mkdirp "^0.5.1" + make-dir "^3.0.0" -jest-validate@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.1.0.tgz#1469fa19f627bb0a9a98e289f3e9ab6a668c732a" - integrity sha512-kGbZq1f02/zVO2+t1KQGSVoCTERc5XeObLwITqC6BTRH3Adv7NZdYqCpKIZLUgpLXf2yISzQ465qOZpul8abXA== +jest-validate@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.2.3.tgz#ecb0f093cf8ae71d15075fb48439b6f78f1fcb5a" + integrity sha512-GObn91jzU0B0Bv4cusAwjP6vnWy78hJUM8MOSz7keRfnac/ZhQWIsUjvk01IfeXNTemCwgR57EtdjQMzFZGREg== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" camelcase "^5.3.1" chalk "^3.0.0" - jest-get-type "^25.1.0" + jest-get-type "^25.2.1" leven "^3.1.0" - pretty-format "^25.1.0" + pretty-format "^25.2.3" -jest-watcher@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.1.0.tgz#97cb4a937f676f64c9fad2d07b824c56808e9806" - integrity sha512-Q9eZ7pyaIr6xfU24OeTg4z1fUqBF/4MP6J801lyQfg7CsnZ/TCzAPvCfckKdL5dlBBEKBeHV0AdyjFZ5eWj4ig== +jest-watcher@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.2.4.tgz#dda85b914d470fa4145164a8f70bda4f208bafb6" + integrity sha512-p7g7s3zqcy69slVzQYcphyzkB2FBmJwMbv6k6KjI5mqd6KnUnQPfQVKuVj2l+34EeuxnbXqnrjtUFmxhcL87rg== dependencies: - "@jest/test-result" "^25.1.0" - "@jest/types" "^25.1.0" + "@jest/test-result" "^25.2.4" + "@jest/types" "^25.2.3" ansi-escapes "^4.2.1" chalk "^3.0.0" - jest-util "^25.1.0" + jest-util "^25.2.3" string-length "^3.1.0" -jest-worker@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.1.0.tgz#75d038bad6fdf58eba0d2ec1835856c497e3907a" - integrity sha512-ZHhHtlxOWSxCoNOKHGbiLzXnl42ga9CxDr27H36Qn+15pQZd3R/F24jrmjDelw9j/iHUIWMWs08/u2QN50HHOg== +jest-worker@^25.2.1: + version "25.2.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.2.1.tgz#209617015c768652646aa33a7828cc2ab472a18a" + integrity sha512-IHnpekk8H/hCUbBlfeaPZzU6v75bqwJp3n4dUrQuQOAgOneI4tx3jV2o8pvlXnDfcRsfkFIUD//HWXpCmR+evQ== dependencies: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-25.1.0.tgz#b85ef1ddba2fdb00d295deebbd13567106d35be9" - integrity sha512-FV6jEruneBhokkt9MQk0WUFoNTwnF76CLXtwNMfsc0um0TlB/LG2yxUd0KqaFjEJ9laQmVWQWS0sG/t2GsuI0w== +jest@^25.2.4: + version "25.2.4" + resolved "https://registry.yarnpkg.com/jest/-/jest-25.2.4.tgz#d10941948a2b57eb7accc2e7ae78af4a0e11b40a" + integrity sha512-Lu4LXxf4+durzN/IFilcAoQSisOwgHIXgl9vffopePpSSwFqfj1Pj4y+k3nL8oTbnvjxgDIsEcepy6he4bWqnQ== dependencies: - "@jest/core" "^25.1.0" + "@jest/core" "^25.2.4" import-local "^3.0.2" - jest-cli "^25.1.0" + jest-cli "^25.2.4" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -2808,7 +2873,7 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^15.1.1: +jsdom@^15.2.1: version "15.2.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== @@ -2865,12 +2930,12 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@2.x, json5@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" - integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== +json5@2.x, json5@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" + integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== dependencies: - minimist "^1.2.0" + minimist "^1.2.5" jsprim@^1.2.2: version "1.4.1" @@ -3128,15 +3193,10 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= - -minimist@^1.1.1, minimist@^1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f" - integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw== +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== mixin-deep@^1.2.0: version "1.3.2" @@ -3146,12 +3206,17 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.x, mkdirp@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= +mkdirp@1.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" + integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== + +mkdirp@^0.5.1: + version "0.5.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" + integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== dependencies: - minimist "0.0.8" + minimist "^1.2.5" mockdate@^2.0.5: version "2.0.5" @@ -3340,14 +3405,6 @@ object.fromentries@^2.0.2: function-bind "^1.1.1" has "^1.0.3" -object.getownpropertydescriptors@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" - integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -3516,9 +3573,9 @@ performance-now@^2.1.0: integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7: - version "2.2.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== pify@^2.0.0: version "2.3.0" @@ -3561,22 +3618,12 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -pretty-format@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" - integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== - dependencies: - "@jest/types" "^24.9.0" - ansi-regex "^4.0.0" - ansi-styles "^3.2.0" - react-is "^16.8.4" - -pretty-format@^25.1.0: - version "25.1.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.1.0.tgz#ed869bdaec1356fc5ae45de045e2c8ec7b07b0c8" - integrity sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ== +pretty-format@^25.1.0, pretty-format@^25.2.3: + version "25.2.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.2.3.tgz#ba6e9603a0d80fa2e470b1fed55de1f9bfd81421" + integrity sha512-IP4+5UOAVGoyqC/DiomOeHBUKN6q00gfyT2qpAsRH64tgOKB2yF7FHJXC18OCiU0/YFierACup/zdCOWw0F/0w== dependencies: - "@jest/types" "^25.1.0" + "@jest/types" "^25.2.3" ansi-regex "^5.0.0" ansi-styles "^4.0.0" react-is "^16.12.0" @@ -3592,9 +3639,9 @@ promise-polyfill@^8.1.3: integrity sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g== prompts@^2.0.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.1.tgz#b63a9ce2809f106fa9ae1277c275b167af46ea05" - integrity sha512-qIP2lQyCwYbdzcqHIUi2HAxiWixhoM9OdLCWf8txXsapC/X9YdsCoeyRIXE/GP+Q0J37Q7+XN/MFqbUa7IzXNA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" + integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== dependencies: kleur "^3.0.3" sisteransi "^1.0.4" @@ -3609,9 +3656,9 @@ prop-types@^15.6.2, prop-types@^15.7.2: react-is "^16.8.1" psl@^1.1.28: - version "1.7.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" - integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ== + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== pump@^3.0.0: version "3.0.0" @@ -3631,15 +3678,15 @@ qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== -react-dom@^16.8.6: - version "16.13.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.0.tgz#cdde54b48eb9e8a0ca1b3dc9943d9bb409b81866" - integrity sha512-y09d2c4cG220DzdlFkPTnVvGTszVvNpC73v+AaLGLHbkpy3SSgvYq8x0rNwPJ/Rk/CicTNgk0hbHNw1gMEZAXg== +react-dom@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.19.0" + scheduler "^0.19.1" react-hooks-testing-library@^0.6.0: version "0.6.0" @@ -3648,25 +3695,25 @@ react-hooks-testing-library@^0.6.0: dependencies: "@testing-library/react-hooks" "~1.0.2" -react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: - version "16.13.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" - integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== +react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.6: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-test-renderer@^16.8.6: - version "16.13.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.0.tgz#39ba3bf72cedc8210c3f81983f0bb061b14a3014" - integrity sha512-NQ2S9gdMUa7rgPGpKGyMcwl1d6D9MCF0lftdI3kts6kkiX+qvpC955jNjAZXlIDTjnN9jwFI8A8XhRh/9v0spA== +react-test-renderer@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1" + integrity sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" react-is "^16.8.6" - scheduler "^0.19.0" + scheduler "^0.19.1" -react@^16.8.6: - version "16.13.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.13.0.tgz#d046eabcdf64e457bbeed1e792e235e1b9934cf7" - integrity sha512-TSavZz2iSLkq5/oiE7gnFzmURKZMltmi193rm5HEoUDAXpzT9Kzw6oNZnGoai/4+fUnm7FqS5dwgUL34TujcWQ== +react@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -3696,12 +3743,10 @@ readdirp@~3.3.0: dependencies: picomatch "^2.0.7" -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== - dependencies: - util.promisify "^1.0.0" +realpath-native@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" + integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== regenerator-runtime@^0.13.4: version "0.13.5" @@ -3925,29 +3970,24 @@ saxes@^3.1.9: dependencies: xmlchars "^2.1.1" -scheduler@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.0.tgz#a715d56302de403df742f4a9be11975b32f5698d" - integrity sha512-xowbVaTPe9r7y7RUejcK73/j8tt2jfiyTednOvHbA8JoClvMYCp+r8QegLwK/n8zWQAtZb1fFnER4XLBZXrCxA== +scheduler@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" + integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.3.0: +semver@6.x, semver@^6.0.0, semver@^6.1.0, 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== -semver@^7.1.1: - version "7.1.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" - integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== - set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -4001,14 +4041,14 @@ side-channel@^1.0.2: object-inspect "^1.7.0" signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== sisteransi@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" - integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig== + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== slash@^3.0.0: version "3.0.0" @@ -4202,21 +4242,39 @@ string.prototype.matchall@^4.0.2: regexp.prototype.flags "^1.3.0" side-channel "^1.0.2" +string.prototype.trimend@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz#ee497fd29768646d84be2c9b819e292439614373" + integrity sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" + +string.prototype.trimstart@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz#afe596a7ce9de905496919406c9734845f01a2f2" + integrity sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" @@ -4399,10 +4457,10 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" -ts-jest@^25.2.1: - version "25.2.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.2.1.tgz#49bf05da26a8b7fbfbc36b4ae2fcdc2fef35c85d" - integrity sha512-TnntkEEjuXq/Gxpw7xToarmHbAafgCaAzOpnajnFC6jI7oo1trMzAHA04eWpc3MhV6+yvhE8uUBAmN+teRJh0A== +ts-jest@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.3.0.tgz#c12d34573cbe34d49f10567940e44fd19d1c9178" + integrity sha512-qH/uhaC+AFDU9JfAueSr0epIFJkGMvUPog4FxSEVAtPOur1Oni5WBJMiQIkfHvc7PviVRsnlVLLY2I6221CQew== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -4410,10 +4468,10 @@ ts-jest@^25.2.1: json5 "2.x" lodash.memoize "4.x" make-error "1.x" - mkdirp "0.x" + mkdirp "1.x" resolve "1.x" - semver "^5.5" - yargs-parser "^16.1.0" + semver "6.x" + yargs-parser "^18.1.1" tslib@^1.8.1, tslib@^1.9.0: version "1.11.1" @@ -4468,7 +4526,7 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^3.4.5: +typescript@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== @@ -4513,16 +4571,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.promisify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - utility-types@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" @@ -4539,9 +4587,9 @@ v8-compile-cache@^2.0.3: integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== v8-to-istanbul@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.2.tgz#387d173be5383dbec209d21af033dcb892e3ac82" - integrity sha512-G9R+Hpw0ITAmPSr47lSlc5A1uekSYzXxTMlFxso2xoffwo4jQnzbv1p9yXIinO8UMZKfAFewaCHwWvnH4Jb4Ug== + version "4.1.3" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20" + integrity sha512-sAjOC+Kki6aJVbUOXJbcR0MnbfjvBzwKZazEJymA2IX49uoOdEdk+4fBq5cXgYgiyKtAyrrJNtBZdOeDIF+Fng== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -4638,7 +4686,7 @@ which@^1.2.9, which@^1.3.1: dependencies: isexe "^2.0.0" -which@^2.0.1: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -4708,26 +4756,18 @@ 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== -yargs-parser@^16.1.0: - version "16.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-16.1.0.tgz#73747d53ae187e7b8dbe333f95714c76ea00ecf1" - integrity sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.0: - version "18.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.0.tgz#1b0ab1118ebd41f68bb30e729f4c83df36ae84c3" - integrity sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ== +yargs-parser@^18.1.1: + version "18.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1" + integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^15.0.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" - integrity sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA== +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== dependencies: cliui "^6.0.0" decamelize "^1.2.0" @@ -4739,4 +4779,4 @@ yargs@^15.0.0: string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^18.1.0" + yargs-parser "^18.1.1"