From baaafc2ca23a223a77a981baf4e6d6b4a0453fb3 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Fri, 18 Feb 2022 18:36:55 +0100 Subject: [PATCH] feat: Support `populateCache` as a function (#1818) * allow populateCache as function * test: add a test for the behavior of revalidateOnMount when the key has been changed (#1847) * add tests * pass current data * update example and deps * remove next.lock Co-authored-by: Toru Kobayashi --- examples/optimistic-ui/libs/fetch.js | 1 + examples/optimistic-ui/pages/_app.js | 5 + examples/optimistic-ui/pages/api/data.js | 22 ---- examples/optimistic-ui/pages/api/todos.js | 30 +++++ examples/optimistic-ui/pages/index.js | 145 +++++++++++++++------ examples/optimistic-ui/styles.css | 91 +++++++++++++ package.json | 10 +- src/types.ts | 4 +- src/use-swr.ts | 4 +- src/utils/helper.ts | 2 +- src/utils/mutate.ts | 18 ++- test/use-swr-local-mutation.test.tsx | 108 ++++++++++++++++ yarn.lock | 151 +++++++++++----------- 13 files changed, 443 insertions(+), 148 deletions(-) create mode 100644 examples/optimistic-ui/pages/_app.js delete mode 100644 examples/optimistic-ui/pages/api/data.js create mode 100644 examples/optimistic-ui/pages/api/todos.js create mode 100644 examples/optimistic-ui/styles.css diff --git a/examples/optimistic-ui/libs/fetch.js b/examples/optimistic-ui/libs/fetch.js index 7675f2c2f..073253a9b 100644 --- a/examples/optimistic-ui/libs/fetch.js +++ b/examples/optimistic-ui/libs/fetch.js @@ -1,4 +1,5 @@ export default async function fetcher(...args) { const res = await fetch(...args) + if (!res.ok) throw new Error('Failed to fetch') return res.json() } diff --git a/examples/optimistic-ui/pages/_app.js b/examples/optimistic-ui/pages/_app.js new file mode 100644 index 000000000..2f8cc070e --- /dev/null +++ b/examples/optimistic-ui/pages/_app.js @@ -0,0 +1,5 @@ +import '../styles.css' + +export default function App({ Component, pageProps }) { + return +} diff --git a/examples/optimistic-ui/pages/api/data.js b/examples/optimistic-ui/pages/api/data.js deleted file mode 100644 index cfb4ef9c6..000000000 --- a/examples/optimistic-ui/pages/api/data.js +++ /dev/null @@ -1,22 +0,0 @@ -const data = [] - -function shouldFail() { - return Math.random() > 0.8 -} - -export default function api(req, res) { - if (req.method === 'POST') { - const body = JSON.parse(req.body) - // sometimes it will fail, this will cause a regression on the UI - if (!shouldFail()) { - data.push(body.text); - } - res.json(data) - return - } - - setTimeout(() => { - res.json(data) - }, 2000) -} - diff --git a/examples/optimistic-ui/pages/api/todos.js b/examples/optimistic-ui/pages/api/todos.js new file mode 100644 index 000000000..ccca949b9 --- /dev/null +++ b/examples/optimistic-ui/pages/api/todos.js @@ -0,0 +1,30 @@ +let todos = [] +const delay = () => new Promise(res => setTimeout(() => res(), 1000)) + +async function getTodos() { + await delay() + return todos.sort((a, b) => (a.text < b.text ? -1 : 1)) +} + +async function addTodo(todo) { + await delay() + // Sometimes it will fail, this will cause a regression on the UI + if (Math.random() < 0.2 || !todo.text) + throw new Error('Failed to add new item!') + todo.text = todo.text.charAt(0).toUpperCase() + todo.text.slice(1) + todos = [...todos, todo] + return todo +} + +export default async function api(req, res) { + try { + if (req.method === 'POST') { + const body = JSON.parse(req.body) + return res.json(await addTodo(body)) + } + + return res.json(await getTodos()) + } catch (err) { + return res.status(500).json({ error: err.message }) + } +} diff --git a/examples/optimistic-ui/pages/index.js b/examples/optimistic-ui/pages/index.js index 89b00c5b8..d45d27ab5 100644 --- a/examples/optimistic-ui/pages/index.js +++ b/examples/optimistic-ui/pages/index.js @@ -1,41 +1,110 @@ -import React from 'react' +import useSWR from 'swr' +import React, { useState } from 'react' + import fetch from '../libs/fetch' -import useSWR, { mutate } from 'swr' - -export default function Index() { - const [text, setText] = React.useState(''); - const { data } = useSWR('/api/data', fetch) - - async function handleSubmit(event) { - event.preventDefault() - // Call mutate to optimistically update the UI. - mutate('/api/data', [...data, text], false) - // Then we send the request to the API and let mutate - // update the data with the API response. - // Our action may fail in the API function, and the response differ - // from what was optimistically updated, in that case the UI will be - // changed to match the API response. - // The fetch could also fail, in that case the UI will - // be in an incorrect state until the next successful fetch. - mutate('/api/data', await fetch('/api/data', { - method: 'POST', - body: JSON.stringify({ text }) - })) - setText('') - } - - return
-
- setText(event.target.value)} - value={text} - /> - -
-
    - {data ? data.map(datum =>
  • {datum}
  • ) : 'loading...'} -
-
+export default function App() { + const [text, setText] = useState('') + const { data, mutate } = useSWR('/api/todos', fetch) + + const [state, setState] = useState( ) + + return ( +
+ {/* */} +

Optimistic UI with SWR

+ +

+ + + + This application optimistically updates the data, while revalidating in + the background. The POST API auto capitializes the data, + and only returns the new added one instead of the full list. And the{' '} + GET API returns the full list in order. +

+ +
ev.preventDefault()}> + setText(e.target.value)} + placeholder="Add your to-do here..." + autoFocus + /> + +
+ + {state} + +
    + {data ? ( + data.length ? ( + data.map(todo => { + return
  • {todo.text}
  • + }) + ) : ( + + No todos yet. Try adding lowercased "banana" and "apple" to the + list. + + ) + ) : ( + Loading... + )} +
+
+ ) } diff --git a/examples/optimistic-ui/styles.css b/examples/optimistic-ui/styles.css new file mode 100644 index 000000000..7883519cc --- /dev/null +++ b/examples/optimistic-ui/styles.css @@ -0,0 +1,91 @@ +html { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, + Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + text-align: center; +} + +body { + max-width: 600px; + margin: auto; +} + +h1 { + margin-top: 1em; +} + +.note { + text-align: left; + font-size: 0.9em; + line-height: 1.5; + color: #666; +} + +.note svg { + margin-right: 0.5em; + vertical-align: -2px; + width: 14px; + height: 14px; + margin-right: 5px; +} + +form { + display: flex; + margin: 8px 0; + gap: 8px; +} + +input { + flex: 1; +} + +input, +button { + font-size: 16px; + padding: 6px 5px; +} + +code { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + Liberation Mono, Courier New, monospace; + font-feature-settings: 'rlig' 1, 'calt' 1, 'ss01' 1; + background-color: #eee; + padding: 1px 3px; + border-radius: 2px; +} + +ul { + text-align: left; + list-style: none; + padding: 0; +} + +li { + margin: 8px 0; + padding: 10px; + border-radius: 4px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12), 0 0 0 1px #ededed; +} + +i { + color: #999; +} + +.info, +.success, +.error { + display: block; + text-align: left; + padding: 6px 0; + font-size: 0.9em; + opacity: 0.9; +} + +.info { + color: #666; +} +.success { + color: #4caf50; +} +.error { + color: #f44336; +} diff --git a/package.json b/package.json index 05e382250..26e79ba01 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "husky": "2.4.1", "jest": "27.0.6", "lint-staged": "8.2.1", - "next": "12.0.9", + "next": "^12.1.0", "npm-run-all": "4.1.5", "prettier": "2.5.0", "react": "17.0.1", @@ -112,11 +112,11 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" }, "prettier": { + "tabWidth": 2, "semi": false, - "singleQuote": true, "useTabs": false, - "trailingComma": "none", - "tabWidth": 2, - "arrowParens": "avoid" + "singleQuote": true, + "arrowParens": "avoid", + "trailingComma": "none" } } diff --git a/src/types.ts b/src/types.ts index 058fdf35d..7029e622c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -139,12 +139,12 @@ export type Arguments = export type Key = Arguments | (() => Arguments) export type MutatorCallback = ( - currentValue?: Data + currentData?: Data ) => Promise | undefined | Data export type MutatorOptions = { revalidate?: boolean - populateCache?: boolean + populateCache?: boolean | ((result: any, currentData: Data) => Data) optimisticData?: Data rollbackOnError?: boolean } diff --git a/src/use-swr.ts b/src/use-swr.ts index e8aa6f08e..113e52f5c 100644 --- a/src/use-swr.ts +++ b/src/use-swr.ts @@ -360,7 +360,9 @@ export const useSWRHandler = ( // eslint-disable-next-line react-hooks/exhaustive-deps const boundMutate: SWRResponse['mutate'] = useCallback( // By using `bind` we don't need to modify the size of the rest arguments. - internalMutate.bind(UNDEFINED, cache, () => keyRef.current), + // Due to https://github.com/microsoft/TypeScript/issues/37181, we have to + // cast it to any for now. + internalMutate.bind(UNDEFINED, cache, () => keyRef.current) as any, // eslint-disable-next-line react-hooks/exhaustive-deps [] ) diff --git a/src/utils/helper.ts b/src/utils/helper.ts index fad835625..68eb108f1 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -4,7 +4,7 @@ export const noop = () => {} // by something else. Prettier ignore and extra parentheses are necessary here // to ensure that tsc doesn't remove the __NOINLINE__ comment. // prettier-ignore -export const UNDEFINED: undefined = (/*#__NOINLINE__*/ noop()) as undefined +export const UNDEFINED = (/*#__NOINLINE__*/ noop()) as undefined export const OBJECT = Object diff --git a/src/utils/mutate.ts b/src/utils/mutate.ts index e6e8a4cdb..630f8e853 100644 --- a/src/utils/mutate.ts +++ b/src/utils/mutate.ts @@ -22,7 +22,9 @@ export const internalMutate = async ( typeof _opts === 'boolean' ? { revalidate: _opts } : _opts || {} // Fallback to `true` if it's not explicitly set to `false` - let populateCache = options.populateCache !== false + let populateCache = isUndefined(options.populateCache) + ? true + : options.populateCache const revalidate = options.revalidate !== false const rollbackOnError = options.rollbackOnError !== false const optimisticData = options.optimisticData @@ -43,7 +45,7 @@ export const internalMutate = async ( UNDEFINED, UNDEFINED, revalidate, - populateCache + true ) } @@ -87,18 +89,26 @@ export const internalMutate = async ( if (error) throw error return data } else if (error && hasOptimisticData && rollbackOnError) { - // Rollback. Always populate the cache in this case. + // Rollback. Always populate the cache in this case but without + // transforming the data. populateCache = true data = rollbackData cache.set(key, rollbackData) } } + // If we should write back the cache after request. if (populateCache) { if (!error) { + // Transform the result into data. + if (isFunction(populateCache)) { + data = populateCache(data, rollbackData) + } + // Only update cached data if there's no error. Data can be `undefined` here. cache.set(key, data) } + // Always update or reset the error. cache.set(keyInfo, mergeObjects(cache.get(keyInfo), { error })) } @@ -114,7 +124,7 @@ export const internalMutate = async ( error, UNDEFINED, revalidate, - populateCache + !!populateCache ) // Throw error or return data diff --git a/test/use-swr-local-mutation.test.tsx b/test/use-swr-local-mutation.test.tsx index f00c49a10..3efbdab3c 100644 --- a/test/use-swr-local-mutation.test.tsx +++ b/test/use-swr-local-mutation.test.tsx @@ -1112,4 +1112,112 @@ describe('useSWR - local mutation', () => { await sleep(30) expect(renderedData).toEqual([undefined, 0, 'bar', 1]) }) + + it('should support transforming the result with `populateCache` before writing back', async () => { + const key = createKey() + function Page() { + const { data, mutate } = useSWR(key, () => 'foo') + return ( + <> +
data: {String(data)}
+ + + ) + } + + renderWithConfig() + await screen.findByText('data: foo') + + fireEvent.click(screen.getByText('mutate')) + await sleep(30) + await screen.findByText('data: !bar') + }) + + it('should support transforming the result with `populateCache` for async data with optimistic data', async () => { + const key = createKey() + const renderedData = [] + + let mutatePage + + function Page() { + const { data, mutate } = useSWR(key, () => 'foo') + mutatePage = () => + mutate(new Promise(res => setTimeout(() => res('baz'), 20)), { + optimisticData: 'bar', + revalidate: false, + populateCache: v => '!' + v + }) + + renderedData.push(data) + return null + } + + renderWithConfig() + await act(() => sleep(10)) + await act(() => mutatePage()) + await sleep(30) + expect(renderedData).toEqual([undefined, 'foo', 'bar', '!baz']) + }) + + it('should pass the original data snapshot to `populateCache` as the second parameter', async () => { + const key = createKey() + const renderedData = [] + + let serverData = ['Apple', 'Banana'] + + let appendData + + const sendRequest = newItem => { + // @TODO: We use `any` here due to limitation of type inference. + return new Promise(res => + setTimeout(() => { + // Server capitializes the new item. + const modifiedData = + newItem.charAt(0).toUpperCase() + newItem.slice(1) + serverData = [...serverData, modifiedData] + res(modifiedData) + }, 20) + ) + } + + function Page() { + const { data, mutate } = useSWR(key, () => serverData) + + appendData = () => { + return mutate(sendRequest('cherry'), { + optimisticData: [...data, 'cherry (optimistic)'], + populateCache: (result, currentData) => [ + ...currentData, + result + ' (res)' + ], + revalidate: true + }) + } + + renderedData.push(data) + return null + } + + renderWithConfig() + await act(() => sleep(10)) + await act(() => appendData()) + await sleep(30) + + expect(renderedData).toEqual([ + undefined, // fetching + ['Apple', 'Banana'], // initial data + ['Apple', 'Banana', 'cherry (optimistic)'], // optimistic data + ['Apple', 'Banana', 'Cherry (res)'], // appended server response + ['Apple', 'Banana', 'Cherry'] // revalidated data + ]) + }) }) diff --git a/yarn.lock b/yarn.lock index f4461cd66..c70b5e6cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1253,65 +1253,65 @@ resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c" integrity sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA== -"@next/env@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.9.tgz#4c9e9eef00226145d9629a846b8cc31878e1328c" - integrity sha512-oBlkyDop0Stf7MPIzETGv5r0YT/G/weBrknoPOUTaa5qwOeGjuy6gsOVc/SBtrBkOoBmRpD+fFhQJPvmo1mS+g== - -"@next/swc-android-arm64@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.0.9.tgz#2cdbcc1814471044ea0e057b475090d25654833c" - integrity sha512-aVqgsEn5plmUH2X58sjzhHsH/6majucWTMaaBEs7hHO2+GCwCZc7zaLH4XCBMKPES9Yaja8/pYUbvZQE9DqgFw== - -"@next/swc-darwin-arm64@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.9.tgz#ea200929d7116de12c6f3b13ff75f9522c2153e3" - integrity sha512-uAgRKm4a2nVdyBiPPJokvmDD1saugOvxljz9ld2ih0CCg5S9vBhqaj3kPGCQBj9hSu3q+Lng2CHnQqG3ga1jzA== - -"@next/swc-darwin-x64@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.9.tgz#32800a7a9aff4bfd2038b0bce3657ece8708a87b" - integrity sha512-fDOs2lZIyrAdU18IxMA5orBPn9qLbOdu55gXSTNZOhyRJ8ugtbUAejsK7OL0boJy0CCHPAdVRXm01Mwk8tZ9RQ== - -"@next/swc-linux-arm-gnueabihf@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.9.tgz#da012dfb69ad2abc3d4045395581b650048bdd7c" - integrity sha512-/ni0p9DBvATUML9RQ1ycQuf05uOYKdzA6iI8+eRsARjpGbFVUFbge7XPzlj9g2Q9YWgoN8CSjFGnKRlyky5uHA== - -"@next/swc-linux-arm64-gnu@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.9.tgz#fe704c0a1cb048ef19d4a24b2c990574c96c933b" - integrity sha512-AphxilJDf95rUxJDHgM9Ww1DaYXZWqTvoKwXeej/0SgSvICcRZrLaFDrkojdXz0Rxr4igX2OdYR1S4/Hj1jWOQ== - -"@next/swc-linux-arm64-musl@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.9.tgz#b2bb68940903cd64f7875979ed9907e946dc4f3e" - integrity sha512-K5jbvNNzF3mRjWmPdxP5Bg87i7FHivfBj/L0KJlxpkLSC8sffBJDmB6jtMnI7wiPj9J6vmLkbGtSosln78xAlQ== - -"@next/swc-linux-x64-gnu@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.9.tgz#b700ba095551d4f6e830b92d4593a3b6e73bba82" - integrity sha512-bJZ9bkMkQzsY+UyWezEZ77GWQ4TzwKeXdayX3U3+aEkL8k5C6eKBXlidWdrhu0teLmaUXIyWerWrLnJzwGXdfw== - -"@next/swc-linux-x64-musl@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.9.tgz#678460266f544b52f1190ef0c3494e436608591e" - integrity sha512-SR9p0R+v1T32DTXPVAXZw31pmJAkSDotC6Afy+mfC0xrEL3pp95R8sGXYAAUCEPkQp0MEeUOVy2LrToe92X7hQ== - -"@next/swc-win32-arm64-msvc@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.9.tgz#f70e5bd0821ca168aeef117e51ab870265ceeeb1" - integrity sha512-mzQ1A8vfHhJrvEy5KJZGZWEByXthyKfWofvFaf+oo/5nJl/0Bz1ODP2ajSmbLG++77Eo2AROgbm9pkW1ucvG2A== - -"@next/swc-win32-ia32-msvc@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.9.tgz#0b853793754642cde9f9099087d4a86b6a99a24d" - integrity sha512-MpD2vj1zjo1u3J3wiz3pEKse19Etz+P0GL6XfQkB/9a84vJQ1JWMaWBjmIdivzZv718Il2pRSSx8hymwPfguYQ== - -"@next/swc-win32-x64-msvc@12.0.9": - version "12.0.9" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.9.tgz#f7d3b59000082cf65c84fdc61930b708aa5446e5" - integrity sha512-1c/sxp/4Qz4F6rCxiYqAnrmghCOFt5hHZ9Kd+rXFW5Mqev4C4XDOUMHdBH55HgnJZqngYhOE0r/XNkCtsIojig== +"@next/env@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314" + integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ== + +"@next/swc-android-arm64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39" + integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA== + +"@next/swc-darwin-arm64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135" + integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg== + +"@next/swc-darwin-x64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd" + integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug== + +"@next/swc-linux-arm-gnueabihf@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7" + integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog== + +"@next/swc-linux-arm64-gnu@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093" + integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q== + +"@next/swc-linux-arm64-musl@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566" + integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA== + +"@next/swc-linux-x64-gnu@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e" + integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A== + +"@next/swc-linux-x64-musl@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31" + integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw== + +"@next/swc-win32-arm64-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283" + integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw== + +"@next/swc-win32-ia32-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1" + integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q== + +"@next/swc-win32-x64-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064" + integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg== "@node-rs/helper@^1.0.0": version "1.2.1" @@ -4962,28 +4962,28 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -next@12.0.9: - version "12.0.9" - resolved "https://registry.yarnpkg.com/next/-/next-12.0.9.tgz#4eb3006b63bb866f5c2918ca0003e98f4259e063" - integrity sha512-omfYqoR/DvbdOIJ6SS1unKJ4mGIxUPs0RPa7wr/Mft22OCKgJhuG+aI9KFYi5ZJBwoFQk1vqaMKpWz5qr+dN0Q== +next@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d" + integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q== dependencies: - "@next/env" "12.0.9" + "@next/env" "12.1.0" caniuse-lite "^1.0.30001283" postcss "8.4.5" styled-jsx "5.0.0" use-subscription "1.5.1" optionalDependencies: - "@next/swc-android-arm64" "12.0.9" - "@next/swc-darwin-arm64" "12.0.9" - "@next/swc-darwin-x64" "12.0.9" - "@next/swc-linux-arm-gnueabihf" "12.0.9" - "@next/swc-linux-arm64-gnu" "12.0.9" - "@next/swc-linux-arm64-musl" "12.0.9" - "@next/swc-linux-x64-gnu" "12.0.9" - "@next/swc-linux-x64-musl" "12.0.9" - "@next/swc-win32-arm64-msvc" "12.0.9" - "@next/swc-win32-ia32-msvc" "12.0.9" - "@next/swc-win32-x64-msvc" "12.0.9" + "@next/swc-android-arm64" "12.1.0" + "@next/swc-darwin-arm64" "12.1.0" + "@next/swc-darwin-x64" "12.1.0" + "@next/swc-linux-arm-gnueabihf" "12.1.0" + "@next/swc-linux-arm64-gnu" "12.1.0" + "@next/swc-linux-arm64-musl" "12.1.0" + "@next/swc-linux-x64-gnu" "12.1.0" + "@next/swc-linux-x64-musl" "12.1.0" + "@next/swc-win32-arm64-msvc" "12.1.0" + "@next/swc-win32-ia32-msvc" "12.1.0" + "@next/swc-win32-x64-msvc" "12.1.0" nice-try@^1.0.4: version "1.0.5" @@ -6265,6 +6265,7 @@ supports-hyperlinks@^2.0.0: "swr@link:.": version "0.0.0" + uid "" symbol-observable@^1.1.0: version "1.2.0"