diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 483392af19..eb804d66fa 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -1,6 +1,6 @@ { "installCommand": "install:csb", - "sandboxes": ["/examples/react/basic", "/examples/react/basic-typescript"], + "sandboxes": ["/examples/react/basic", "/examples/react/basic-typescript", "/examples/solid/basic-typescript"], "packages": ["packages/**"], "node": "16" } diff --git a/babel.config.js b/babel.config.js index 8503e70bcb..1c7384078a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -17,7 +17,6 @@ module.exports = { }, ], '@babel/preset-typescript', - '@babel/react', ], plugins: [ cjs && ['@babel/transform-modules-commonjs', { loose }], @@ -32,4 +31,14 @@ module.exports = { }, ], ].filter(Boolean), + overrides: [ + { + exclude: './packages/solid-query/**', + presets: ['@babel/react'], + }, + { + include: './packages/solid-query/**', + presets: ['babel-preset-solid'], + }, + ], } diff --git a/docs/adapters/solid-query.md b/docs/adapters/solid-query.md index 02048223e5..bf49538831 100644 --- a/docs/adapters/solid-query.md +++ b/docs/adapters/solid-query.md @@ -1,10 +1,205 @@ --- -title: Solid Query (Coming Soon) +title: Solid Query --- +The `@tanstack/solid-query` package provides a 1st-class API for using TanStack Query with SolidJS. -> ⚠️ This module has not yet been developed. It requires an adapter similar to `react-query` to work. We estimate the amount of code to do this is low-to-moderate, but does require familiarity with the SolidJS framework. If you would like to contribute this adapter, please open a PR! +## Example -The `@tanstack/solid-query` package offers a 1st-class API for using TanStack Query via Solid. However, all of the primitives you receive from this API are core APIs that are shared across all of the TanStack Adapters including the Query Client, query results, query subscriptions, etc. +```tsx +import { QueryClient, QueryClientProvider, createQuery } from '@tanstack/solid-query' +import { Switch, Match, For } from 'solid-js' +const queryClient = new QueryClient() +function Example() { + const query = createQuery(() => ['todos'], fetchTodos) + + return ( +
+ + +

Loading...

+
+ +

Error: {query.error.message}

+
+ + + {(todo) =>

{todo.title}

} +
+
+
+
+ ) +} + +function App() { + return ( + + + + ) +} + +``` + +## Available Functions + +Solid Query offers useful primitives and functions that will make managing server state in SolidJS apps easier. + +- `createQuery` +- `createQueries` +- `createInfiniteQueries` +- `createMutation` +- `useIsFetching` +- `useIsMutating` +- `useQueryClient` +- `QueryClient` +- `QueryClientProvider` + + + + +## Important Differences between Solid Query & React Query + +Solid Query offers an API similar to React Query, but there are some key differences to be mindful of. + +- To maintain their reactivity, Query keys need to be wrapped inside a function while using `createQuery`, `createQueries`, `createInfiniteQuery` and `useIsFetching`. + +```tsx +// ❌ react version +useQuery(["todos", todo], fetchTodos) + +// ✅ solid version +createQuery(() => ["todos", todo()], fetchTodos) +``` + +- Suspense works for queries out of the box if you access the query data inside a `` boundary. + +```tsx +import { For, Suspense } from 'solid-js' + +function Example() { + const query = createQuery(() => ['todos'], fetchTodos) + return ( +
+ {/* ✅ Will trigger loading fallback, data accessed in a suspense context. */} + + {(todo) =>
{todo.title}
}
+
+ {/* ❌ Will not trigger loading fallback, data not accessed in a suspense context. */} + {(todo) =>
{todo.title}
}
+
+ ) +} +``` + +- Solid Query primitives (`createX`) do not support destructuring. The return value from these functions is a store, and their properties are only tracked in a reactive context. + +```tsx +import { QueryClient, QueryClientProvider, createQuery } from '@tanstack/solid-query' +import { Match, Switch } from 'solid-js' + +const queryClient = new QueryClient() + +export default function App() { + return ( + + + + ) +} + +function Example() { + // ❌ react version -- supports destructing outside reactive context + // const { isLoading, error, data } = useQuery(['repoData'], () => + // fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res => + // res.json() + // ) + // ) + + // ✅ solid version -- does not support destructuring outside reactive context + const query = createQuery( + () => ['repoData'], + () => + fetch('https://api.github.com/repos/tannerlinsley/react-query').then( + (res) => res.json(), + ), + ) + + // ✅ access query properties in JSX reactive context + return ( + + Loading... + Error: {query.error.message} + +
+

{query.data.name}

+

{query.data.description}

+ 👀 {query.data.subscribers_count}{' '} + ✨ {query.data.stargazers_count}{' '} + 🍴 {query.data.forks_count} +
+
+
+ ) +} +``` + +- If you want options to be reactive you need to pass them using object getter syntax. This may look strange at first but it leads to more idiomatic solid code. + +```tsx +import { + QueryClient, + QueryClientProvider, + createQuery, +} from '@tanstack/solid-query' +import { createSignal, For } from 'solid-js' + +const queryClient = new QueryClient() + +function Example() { + const [enabled, setEnabled] = createSignal(false) + const query = createQuery(() => ['todos'], fetchTodos, { + // ❌ passing a signal directly is not reactive + // enabled: enabled(), + + // ✅ passing a function that returns a signal is reactive + get enabled() { + return enabled() + }, + }) + + return ( +
+ + +

Loading...

+
+ +

Error: {query.error.message}

+
+ + + {(todo) =>

{todo.title}

} +
+
+
+ +
+ ) +} + +function App() { + return ( + + + + ) +} +``` + +- Errors can be caught and reset using SolidJS' native `ErrorBoundary` component. `QueryErrorResetBoundary` is not needed with Solid Query + +- Since Property tracking is handled through Solid's fine grained reactivity, options like `notifyOnChangeProps` are not needed \ No newline at end of file diff --git a/examples/solid/README.md b/examples/solid/README.md new file mode 100644 index 0000000000..30d1ce8a29 --- /dev/null +++ b/examples/solid/README.md @@ -0,0 +1,22 @@ +# Examples to Add + +- auto-refetching 🚫 nextjs (react only) +- basic 🚫 javascript (only converting typescript) +- basic-graphql-request 🟢 +- basic-typescript 🟢 +- custom-hooks 🚫 not implemented in react +- default-query-function 🟡 (green styling for cached post not working) +- focus-refetching 🚫 not implemented in react +- load-more-infinite-scroll 🚫 nextjs (react only) +- nextjs 🚫 nextjs (react only) +- offline 🔴 +- optimistic-updates 🚫 not implemented in react +- optimistic-updates-typescript 🚫 nextjs (react only) +- pagination 🚫 nextjs (react only) +- playground 🔴 +- prefetching 🚫 nextjs (react only) +- react-native 🚫 react native (react only) +- rick-morty 🔴 +- simple 🟢 +- star-wars 🔴 +- suspense 🔴 diff --git a/examples/solid/basic-graphql-request/.eslintrc b/examples/solid/basic-graphql-request/.eslintrc new file mode 100644 index 0000000000..86b22fec59 --- /dev/null +++ b/examples/solid/basic-graphql-request/.eslintrc @@ -0,0 +1,10 @@ +{ + "parserOptions": { + "project": "./tsconfig.json", + "sourceType": "module" + }, + "rules": { + "react/react-in-jsx-scope": "off", + "jsx-a11y/anchor-is-valid": "off" + } +} diff --git a/examples/solid/basic-graphql-request/.gitignore b/examples/solid/basic-graphql-request/.gitignore new file mode 100644 index 0000000000..001e3f924b --- /dev/null +++ b/examples/solid/basic-graphql-request/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.yalc +yalc.lock \ No newline at end of file diff --git a/examples/solid/basic-graphql-request/README.md b/examples/solid/basic-graphql-request/README.md new file mode 100644 index 0000000000..310f37f62f --- /dev/null +++ b/examples/solid/basic-graphql-request/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run start` diff --git a/examples/solid/basic-graphql-request/index.html b/examples/solid/basic-graphql-request/index.html new file mode 100644 index 0000000000..48c59fc124 --- /dev/null +++ b/examples/solid/basic-graphql-request/index.html @@ -0,0 +1,16 @@ + + + + + + + + Solid App + + + +
+ + + + diff --git a/examples/solid/basic-graphql-request/package.json b/examples/solid/basic-graphql-request/package.json new file mode 100644 index 0000000000..c8526451a1 --- /dev/null +++ b/examples/solid/basic-graphql-request/package.json @@ -0,0 +1,24 @@ +{ + "name": "@tanstack/query-example-solid-basic-graphql-request", + "private": true, + "version": "0.0.0", + "description": "", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "graphql": "^16.6.0", + "graphql-request": "^5.0.0", + "solid-js": "^1.5.1" + }, + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "2.3.6" + } +} diff --git a/examples/solid/basic-graphql-request/src/assets/favicon.ico b/examples/solid/basic-graphql-request/src/assets/favicon.ico new file mode 100644 index 0000000000..b836b2bcca Binary files /dev/null and b/examples/solid/basic-graphql-request/src/assets/favicon.ico differ diff --git a/examples/solid/basic-graphql-request/src/index.tsx b/examples/solid/basic-graphql-request/src/index.tsx new file mode 100644 index 0000000000..08f46ab5fc --- /dev/null +++ b/examples/solid/basic-graphql-request/src/index.tsx @@ -0,0 +1,168 @@ +/* @refresh reload */ +import { + createQuery, + QueryClient, + QueryClientProvider, + useQueryClient, +} from '@tanstack/solid-query' +import { Accessor, createSignal, For, Match, Setter, Switch } from 'solid-js' +import { render } from 'solid-js/web' +import { request, gql } from 'graphql-request' + +const endpoint = 'https://graphqlzero.almansi.me/api' + +const queryClient = new QueryClient() + +function App() { + const [postId, setPostId] = createSignal(-1) + + return ( + +

+ As you visit the posts below, you will notice them in a loading state + the first time you load them. However, after you return to this list and + click on any posts you have already visited again, you will see them + load instantly and background refresh right before your eyes!{' '} + + (You may need to throttle your network speed to simulate longer + loading sequences) + +

+ {postId() > -1 ? ( + + ) : ( + + )} +
+ ) +} + +function createPosts() { + return createQuery( + () => ['posts'], + async () => { + const { + posts: { data }, + } = await request( + endpoint, + gql` + query { + posts { + data { + id + title + } + } + } + `, + ) + return data + }, + ) +} + +function Posts(props: { setPostId: Setter }) { + const queryClient = useQueryClient() + const state = createPosts() + + return ( +
+

Posts

+
+ + Loading... + + Error: {(state.error as Error).message} + + + <> + +
{state.isFetching ? 'Background Updating...' : ' '}
+ +
+
+
+
+ ) +} + +function createPost(postId: Accessor) { + return createQuery( + () => ['post', postId()], + async (context) => { + const { post } = await request( + endpoint, + gql` + query { + post(id: ${context.queryKey[1]}) { + id + title + body + } + } + `, + ) + + return post + }, + { + enabled: !!postId, + }, + ) +} + +function Post(props: { postId: number; setPostId: Setter }) { + const state = createPost(() => props.postId) + + return ( +
+ + + + Loading... + + + Error: {(state.error as Error).message} + + + <> +

{state.data.title}

+
+

{state.data.body}

+
+
{state.isFetching ? 'Background Updating...' : ' '}
+ +
+
+
+ ) +} + +render(() => , document.getElementById('root') as HTMLElement) diff --git a/examples/solid/basic-graphql-request/tsconfig.json b/examples/solid/basic-graphql-request/tsconfig.json new file mode 100644 index 0000000000..249b2732a7 --- /dev/null +++ b/examples/solid/basic-graphql-request/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "types": ["vite/client"], + "noEmit": true, + "isolatedModules": true + } +} diff --git a/examples/solid/basic-graphql-request/vite.config.ts b/examples/solid/basic-graphql-request/vite.config.ts new file mode 100644 index 0000000000..598cdda908 --- /dev/null +++ b/examples/solid/basic-graphql-request/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import solidPlugin from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solidPlugin()], + server: { + port: 3000, + }, + build: { + target: 'esnext', + }, + resolve: { + preserveSymlinks: true, + }, +}) diff --git a/examples/solid/basic-typescript/.eslintrc b/examples/solid/basic-typescript/.eslintrc new file mode 100644 index 0000000000..86b22fec59 --- /dev/null +++ b/examples/solid/basic-typescript/.eslintrc @@ -0,0 +1,10 @@ +{ + "parserOptions": { + "project": "./tsconfig.json", + "sourceType": "module" + }, + "rules": { + "react/react-in-jsx-scope": "off", + "jsx-a11y/anchor-is-valid": "off" + } +} diff --git a/examples/solid/basic-typescript/.gitignore b/examples/solid/basic-typescript/.gitignore new file mode 100644 index 0000000000..001e3f924b --- /dev/null +++ b/examples/solid/basic-typescript/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.yalc +yalc.lock \ No newline at end of file diff --git a/examples/solid/basic-typescript/README.md b/examples/solid/basic-typescript/README.md new file mode 100644 index 0000000000..310f37f62f --- /dev/null +++ b/examples/solid/basic-typescript/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run start` diff --git a/examples/solid/basic-typescript/index.html b/examples/solid/basic-typescript/index.html new file mode 100644 index 0000000000..48c59fc124 --- /dev/null +++ b/examples/solid/basic-typescript/index.html @@ -0,0 +1,16 @@ + + + + + + + + Solid App + + + +
+ + + + diff --git a/examples/solid/basic-typescript/package.json b/examples/solid/basic-typescript/package.json new file mode 100644 index 0000000000..8b8e13ef6a --- /dev/null +++ b/examples/solid/basic-typescript/package.json @@ -0,0 +1,22 @@ +{ + "name": "@tanstack/query-example-solid-basic-typescript", + "private": true, + "version": "0.0.0", + "description": "", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1" + }, + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "2.3.6" + } +} diff --git a/examples/solid/basic-typescript/src/assets/favicon.ico b/examples/solid/basic-typescript/src/assets/favicon.ico new file mode 100644 index 0000000000..b836b2bcca Binary files /dev/null and b/examples/solid/basic-typescript/src/assets/favicon.ico differ diff --git a/examples/solid/basic-typescript/src/index.tsx b/examples/solid/basic-typescript/src/index.tsx new file mode 100644 index 0000000000..34d954c74a --- /dev/null +++ b/examples/solid/basic-typescript/src/index.tsx @@ -0,0 +1,163 @@ +/* @refresh reload */ +import { + createQuery, + QueryClient, + QueryClientProvider, + useQueryClient, +} from '@tanstack/solid-query' +import { Component, createSignal, For, Match, Setter, Switch } from 'solid-js' +import { render } from 'solid-js/web' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + cacheTime: 1000 * 60 * 60 * 24, // 24 hours + }, + }, +}) + +type Post = { + id: number + title: string + body: string +} + +function createPosts() { + return createQuery( + () => ['posts'], + async (): Promise> => { + const response = await fetch( + 'https://jsonplaceholder.typicode.com/posts', + { + method: 'GET', + }, + ) + return response.json() + }, + ) +} + +function Posts(props: { setPostId: Setter }) { + const queryClient = useQueryClient() + const state = createPosts() + + return ( + + ) +} + +const getPostById = async (id: number): Promise => { + const response = await fetch( + `https://jsonplaceholder.typicode.com/posts/${id}`, + { + method: 'GET', + }, + ) + return await response.json() +} + +function createPost(postId: number) { + return createQuery( + () => ['post', postId], + () => getPostById(postId), + { + enabled: !!postId, + }, + ) +} + +function Post(props: { postId: number; setPostId: Setter }) { + const state = createPost(props.postId) + + return ( +
+ + + + Loading... + + + Error: {(state.error as Error).message} + + + <> +

{state.data?.title}

+
+

{state.data?.body}

+
+
{state.isFetching ? 'Background Updating...' : ' '}
+ +
+
+
+ ) +} + +const App: Component = () => { + const [postId, setPostId] = createSignal(-1) + + return ( + +

+ As you visit the posts below, you will notice them in a loading state + the first time you load them. However, after you return to this list and + click on any posts you have already visited again, you will see them + load instantly and background refresh right before your eyes!{' '} + + (You may need to throttle your network speed to simulate longer + loading sequences) + +

+ {postId() > -1 ? ( + + ) : ( + + )} +
+ ) +} + +render(() => , document.getElementById('root') as HTMLElement) diff --git a/examples/solid/basic-typescript/tsconfig.json b/examples/solid/basic-typescript/tsconfig.json new file mode 100644 index 0000000000..249b2732a7 --- /dev/null +++ b/examples/solid/basic-typescript/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "types": ["vite/client"], + "noEmit": true, + "isolatedModules": true + } +} diff --git a/examples/solid/basic-typescript/vite.config.ts b/examples/solid/basic-typescript/vite.config.ts new file mode 100644 index 0000000000..598cdda908 --- /dev/null +++ b/examples/solid/basic-typescript/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import solidPlugin from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solidPlugin()], + server: { + port: 3000, + }, + build: { + target: 'esnext', + }, + resolve: { + preserveSymlinks: true, + }, +}) diff --git a/examples/solid/default-query-function/.eslintrc b/examples/solid/default-query-function/.eslintrc new file mode 100644 index 0000000000..86b22fec59 --- /dev/null +++ b/examples/solid/default-query-function/.eslintrc @@ -0,0 +1,10 @@ +{ + "parserOptions": { + "project": "./tsconfig.json", + "sourceType": "module" + }, + "rules": { + "react/react-in-jsx-scope": "off", + "jsx-a11y/anchor-is-valid": "off" + } +} diff --git a/examples/solid/default-query-function/.gitignore b/examples/solid/default-query-function/.gitignore new file mode 100644 index 0000000000..001e3f924b --- /dev/null +++ b/examples/solid/default-query-function/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.yalc +yalc.lock \ No newline at end of file diff --git a/examples/solid/default-query-function/README.md b/examples/solid/default-query-function/README.md new file mode 100644 index 0000000000..310f37f62f --- /dev/null +++ b/examples/solid/default-query-function/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run start` diff --git a/examples/solid/default-query-function/index.html b/examples/solid/default-query-function/index.html new file mode 100644 index 0000000000..48c59fc124 --- /dev/null +++ b/examples/solid/default-query-function/index.html @@ -0,0 +1,16 @@ + + + + + + + + Solid App + + + +
+ + + + diff --git a/examples/solid/default-query-function/package.json b/examples/solid/default-query-function/package.json new file mode 100644 index 0000000000..33b54029fa --- /dev/null +++ b/examples/solid/default-query-function/package.json @@ -0,0 +1,22 @@ +{ + "name": "@tanstack/query-example-solid-default-query-function", + "private": true, + "version": "0.0.0", + "description": "", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1" + }, + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "2.3.6" + } +} diff --git a/examples/solid/default-query-function/src/assets/favicon.ico b/examples/solid/default-query-function/src/assets/favicon.ico new file mode 100644 index 0000000000..b836b2bcca Binary files /dev/null and b/examples/solid/default-query-function/src/assets/favicon.ico differ diff --git a/examples/solid/default-query-function/src/index.tsx b/examples/solid/default-query-function/src/index.tsx new file mode 100644 index 0000000000..a7fb9b1470 --- /dev/null +++ b/examples/solid/default-query-function/src/index.tsx @@ -0,0 +1,138 @@ +/* @refresh reload */ +import { + createQuery, + QueryClient, + QueryClientProvider, + QueryFunction, + useQueryClient, +} from '@tanstack/solid-query' +import { createSignal, For, Match, Setter, Show, Switch } from 'solid-js' +import { render } from 'solid-js/web' + +// Define a default query function that will receive the query key +const defaultQueryFn: QueryFunction = async ({ queryKey }) => { + const response = await fetch( + `https://jsonplaceholder.typicode.com${queryKey[0]}`, + { + method: 'GET', + }, + ) + return response.json() +} + +// provide the default query function to your app via the query client +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, + }, +}) + +function App() { + const [postId, setPostId] = createSignal(-1) + + return ( + +

+ As you visit the posts below, you will notice them in a loading state + the first time you load them. However, after you return to this list and + click on any posts you have already visited again, you will see them + load instantly and background refresh right before your eyes!{' '} + + (You may need to throttle your network speed to simulate longer + loading sequences) + +

+ -1} fallback={}> + + +
+ ) +} + +function Posts(props: { setPostId: Setter }) { + const queryClient = useQueryClient() + + // All you have to do now is pass a key! + const state = createQuery(() => ['/posts']) + + return ( +
+

Posts

+ +
+ ) +} + +function Post(props: { postId: number; setPostId: Setter }) { + // You can even leave out the queryFn and just go straight into options + const state = createQuery(() => [`/posts/${props.postId}`], { + enabled: !!props.postId, + }) + + return ( +
+ + + + Loading... + + + Error: {(state.error as Error).message} + + + <> +

{state.data.title}

+
+

{state.data.body}

+
+
{state.isFetching ? 'Background Updating...' : ' '}
+ +
+
+
+ ) +} + +render(() => , document.getElementById('root') as HTMLElement) diff --git a/examples/solid/default-query-function/tsconfig.json b/examples/solid/default-query-function/tsconfig.json new file mode 100644 index 0000000000..249b2732a7 --- /dev/null +++ b/examples/solid/default-query-function/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "types": ["vite/client"], + "noEmit": true, + "isolatedModules": true + } +} diff --git a/examples/solid/default-query-function/vite.config.ts b/examples/solid/default-query-function/vite.config.ts new file mode 100644 index 0000000000..598cdda908 --- /dev/null +++ b/examples/solid/default-query-function/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import solidPlugin from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solidPlugin()], + server: { + port: 3000, + }, + build: { + target: 'esnext', + }, + resolve: { + preserveSymlinks: true, + }, +}) diff --git a/examples/solid/simple/.eslintrc b/examples/solid/simple/.eslintrc new file mode 100644 index 0000000000..f40d2bc7f1 --- /dev/null +++ b/examples/solid/simple/.eslintrc @@ -0,0 +1,10 @@ +{ + "parserOptions": { + "project": "./tsconfig.json", + "sourceType": "module" + }, + "rules": { + "react/react-in-jsx-scope": "off", + "jsx-a11y/accessible-emoji": "off" + } +} diff --git a/examples/solid/simple/.gitignore b/examples/solid/simple/.gitignore new file mode 100644 index 0000000000..001e3f924b --- /dev/null +++ b/examples/solid/simple/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.yalc +yalc.lock \ No newline at end of file diff --git a/examples/solid/simple/README.md b/examples/solid/simple/README.md new file mode 100644 index 0000000000..310f37f62f --- /dev/null +++ b/examples/solid/simple/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` +- `npm run start` diff --git a/examples/solid/simple/index.html b/examples/solid/simple/index.html new file mode 100644 index 0000000000..48c59fc124 --- /dev/null +++ b/examples/solid/simple/index.html @@ -0,0 +1,16 @@ + + + + + + + + Solid App + + + +
+ + + + diff --git a/examples/solid/simple/package.json b/examples/solid/simple/package.json new file mode 100644 index 0000000000..aa8cf2b8ce --- /dev/null +++ b/examples/solid/simple/package.json @@ -0,0 +1,22 @@ +{ + "name": "@tanstack/query-example-solid-simple", + "private": true, + "version": "0.0.0", + "description": "", + "scripts": { + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1" + }, + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "2.3.6" + } +} diff --git a/examples/solid/simple/src/assets/favicon.ico b/examples/solid/simple/src/assets/favicon.ico new file mode 100644 index 0000000000..b836b2bcca Binary files /dev/null and b/examples/solid/simple/src/assets/favicon.ico differ diff --git a/examples/solid/simple/src/index.tsx b/examples/solid/simple/src/index.tsx new file mode 100644 index 0000000000..bdbf3be480 --- /dev/null +++ b/examples/solid/simple/src/index.tsx @@ -0,0 +1,53 @@ +/* @refresh reload */ +import { + createQuery, + QueryClient, + QueryClientProvider, +} from '@tanstack/solid-query' +import { Match, Switch } from 'solid-js' +import { render } from 'solid-js/web' + +const queryClient = new QueryClient() + +export default function App() { + return ( + + + + ) +} + +function Example() { + const state = createQuery( + () => ['repoData'], + async () => { + const response = await fetch( + 'https://api.github.com/repos/tannerlinsley/react-query', + { + method: 'GET', + }, + ) + return response.json() + }, + ) + + return ( + + Loading... + + {'An error has occurred: ' + (state.error as Error).message} + + +
+

{state.data.name}

+

{state.data.description}

+ 👀 {state.data.subscribers_count}{' '} + ✨ {state.data.stargazers_count}{' '} + 🍴 {state.data.forks_count} +
{state.isFetching ? 'Updating...' : ''}
+
+
+
+ ) +} +render(() => , document.getElementById('root') as HTMLElement) diff --git a/examples/solid/simple/tsconfig.json b/examples/solid/simple/tsconfig.json new file mode 100644 index 0000000000..249b2732a7 --- /dev/null +++ b/examples/solid/simple/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "types": ["vite/client"], + "noEmit": true, + "isolatedModules": true + } +} diff --git a/examples/solid/simple/vite.config.ts b/examples/solid/simple/vite.config.ts new file mode 100644 index 0000000000..598cdda908 --- /dev/null +++ b/examples/solid/simple/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vite' +import solidPlugin from 'vite-plugin-solid' + +export default defineConfig({ + plugins: [solidPlugin()], + server: { + port: 3000, + }, + build: { + target: 'esnext', + }, + resolve: { + preserveSymlinks: true, + }, +}) diff --git a/jest.config.ts b/jest.config.ts index 508d426f73..444d590c6e 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -24,7 +24,7 @@ const moduleNameMapper = { module.exports = { collectCoverage: true, coverageReporters: ['json', 'lcov', 'text', 'clover', 'text-summary'], - projects: packages.map((d) => ({ + projects: packages.map((d: string) => ({ displayName: d, clearMocks: true, testEnvironment: 'jsdom', @@ -34,5 +34,6 @@ module.exports = { printBasicPrototype: false, }, moduleNameMapper, + preset: d.includes("solid") ? 'solid-jest/preset/browser' : undefined, })), } diff --git a/package-lock.json b/package-lock.json index 98aa65a9ab..146c6f28b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "./packages/react-query", "./packages/react-query-devtools", "./packages/react-query-persist-client", + "./packages/solid-query", "./examples/react/auto-refetching", "./examples/react/basic", "./examples/react/basic-graphql-request", @@ -30,7 +31,11 @@ "./examples/react/rick-morty", "./examples/react/simple", "./examples/react/star-wars", - "./examples/react/suspense" + "./examples/react/suspense", + "./examples/solid/basic-typescript", + "./examples/solid/simple", + "./examples/solid/basic-graphql-request", + "./examples/solid/default-query-function" ], "devDependencies": { "@babel/core": "^7.17.9", @@ -58,6 +63,7 @@ "axios": "^0.26.1", "babel-eslint": "^10.1.0", "babel-plugin-transform-async-to-promises": "^0.8.18", + "babel-preset-solid": "^1.5.4", "bundlewatch": "^0.3.2", "concurrently": "^7.1.0", "current-git-branch": "^1.1.0", @@ -90,7 +96,9 @@ "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-visualizer": "^5.6.0", - "solid-js": "^1.3.15", + "solid-jest": "^0.2.0", + "solid-js": "^1.5.4", + "solid-testing-library": "^0.3.0", "stream-to-array": "^2.3.0", "svelte": "^3.48.0", "ts-node": "^10.7.0", @@ -103,8 +111,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -123,8 +131,8 @@ "name": "@tanstack/query-example-react-basic", "version": "0.0.1", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "react": "^18.0.0", "react-dom": "^18.0.0" @@ -138,8 +146,8 @@ "name": "@tanstack/query-example-react-basic-graphql-request", "version": "0.0.1", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "graphql": "^15.3.0", "graphql-request": "^3.1.0", "react": "^18.0.0", @@ -150,127 +158,14 @@ "vite": "^3.0.0" } }, - "examples/react/basic-graphql-request/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, - "examples/react/basic-graphql-request/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/basic-graphql-request/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/basic-graphql-request/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/basic-graphql-request/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/basic-graphql-request/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "examples/react/basic-typescript": { "name": "@tanstack/query-example-react-basic-typescript", "version": "0.0.1", "dependencies": { - "@tanstack/query-sync-storage-persister": "4.3.8", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "@tanstack/react-query-persist-client": "4.3.8", + "@tanstack/query-sync-storage-persister": "4.3.9", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "@tanstack/react-query-persist-client": "4.3.9", "axios": "^0.26.1", "broadcast-channel": "^3.4.1", "react": "^18.0.0", @@ -338,27 +233,6 @@ "eslint": ">=7.0.0" } }, - "examples/react/basic/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, "examples/react/basic/node_modules/axios": { "version": "0.21.4", "license": "MIT", @@ -366,98 +240,6 @@ "follow-redirects": "^1.14.0" } }, - "examples/react/basic/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/basic/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/basic/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/basic/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/basic/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "examples/react/custom-hooks": { "name": "@tanstack/query-example-react-custom-hooks", "version": "0.0.1", @@ -484,8 +266,8 @@ "name": "@tanstack/query-example-react-default-query-function", "version": "0.0.1", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.26.1", "react": "^18.0.0", "react-dom": "^18.0.0" @@ -495,119 +277,6 @@ "vite": "^3.0.0" } }, - "examples/react/default-query-function/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, - "examples/react/default-query-function/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/default-query-function/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/default-query-function/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/default-query-function/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/default-query-function/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "examples/react/focus-refetching": { "name": "@tanstack/query-example-react-focus-revalidate", "version": "1.0.0", @@ -628,8 +297,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -650,8 +319,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "ky": "^0.23.0", "ky-universal": "^0.8.2", "next": "12.2.2", @@ -666,11 +335,11 @@ "name": "@tanstack/query-example-react-offline", "version": "0.0.1", "dependencies": { - "@tanstack/query-sync-storage-persister": "4.3.8", + "@tanstack/query-sync-storage-persister": "4.3.9", "@tanstack/react-location": "^3.7.0", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "@tanstack/react-query-persist-client": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "@tanstack/react-query-persist-client": "4.3.9", "ky": "^0.30.0", "msw": "^0.39.2", "react": "^18.0.0", @@ -682,27 +351,6 @@ "vite": "^3.0.0" } }, - "examples/react/offline/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, "examples/react/offline/node_modules/ky": { "version": "0.30.0", "license": "MIT", @@ -713,98 +361,6 @@ "url": "https://github.com/sindresorhus/ky?sponsor=1" } }, - "examples/react/offline/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/offline/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/offline/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/offline/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/offline/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "examples/react/optimistic-updates": { "name": "@tanstack/query-example-react-optimistic-updates", "version": "1.0.0", @@ -825,8 +381,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@types/node": "14.14.14", "@types/react": "^18.0.15", "axios": "^0.21.1", @@ -853,8 +409,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -873,8 +429,8 @@ "name": "@tanstack/query-example-react-playground", "version": "0.0.1", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "react": "^18.0.0", "react-dom": "^18.0.0" }, @@ -883,126 +439,13 @@ "vite": "^3.0.0" } }, - "examples/react/playground/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, - "examples/react/playground/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/playground/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/playground/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/playground/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/playground/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, "examples/react/prefetching": { "name": "@tanstack/query-example-react-prefetching", "version": "1.0.0", "license": "MIT", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -1024,8 +467,8 @@ "@react-native-community/netinfo": "6.0.2", "@react-navigation/native": "^6.0.2", "@react-navigation/stack": "^6.0.2", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "expo": "~43.0.2", "expo-constants": "~12.1.3", "expo-status-bar": "~1.1.0", @@ -1103,615 +546,121 @@ "object-assign": "^4.1.1" } }, - "examples/react/react-native/node_modules/typescript": { - "version": "4.4.4", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "examples/react/react-router": { - "name": "@tanstack/query-example-react-router", - "version": "0.0.1", - "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "localforage": "^1.10.0", - "match-sorter": "^6.3.1", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "react-router-dom": "^6.4.0", - "rooks": "^6.4.3", - "sort-by": "^1.2.0" - }, - "devDependencies": { - "@vitejs/plugin-react": "^2.0.0", - "vite": "^3.0.0" - } - }, - "examples/react/react-router/node_modules/@vitejs/plugin-react": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.1.tgz", - "integrity": "sha512-uINzNHmjrbunlFtyVkST6lY1ewSfz/XwLufG0PIqvLGnpk2nOIOa/1CACTDNcKi1/RwaCzJLmsXwm1NsUVV/NA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.10", - "@babel/plugin-transform-react-jsx": "^7.18.10", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, - "examples/react/react-router/node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "examples/react/react-router/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, - "examples/react/react-router/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/react-router/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/react-router/node_modules/postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/react-router/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/react-router/node_modules/react-router": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.0.tgz", - "integrity": "sha512-B+5bEXFlgR1XUdHYR6P94g299SjrfCBMmEDJNcFbpAyRH1j1748yt9NdDhW3++nw1lk3zQJ6aOO66zUx3KlTZg==", - "dependencies": { - "@remix-run/router": "1.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "examples/react/react-router/node_modules/react-router-dom": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.0.tgz", - "integrity": "sha512-4Aw1xmXKeleYYQ3x0Lcl2undHR6yMjXZjd9DKZd53SGOYqirrUThyUb0wwAX5VZAyvSuzjNJmZlJ3rR9+/vzqg==", - "dependencies": { - "react-router": "6.4.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "examples/react/react-router/node_modules/terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "examples/react/react-router/node_modules/vite": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.9.tgz", - "integrity": "sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.16", - "resolve": "^1.22.1", - "rollup": ">=2.75.6 <2.77.0 || ~2.77.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "examples/react/rick-morty": { - "name": "@tanstack/query-example-react-rick-morty", - "version": "0.0.1", - "dependencies": { - "@material-ui/core": "^4.9.7", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "react-router": "^5.1.2", - "react-router-dom": "^5.1.2" - }, - "devDependencies": { - "@vitejs/plugin-react": "^2.0.0", - "vite": "^3.0.0" - } - }, - "examples/react/rick-morty/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, - "examples/react/rick-morty/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/rick-morty/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/rick-morty/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/rick-morty/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/rick-morty/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "examples/react/simple": { - "name": "@tanstack/query-example-react-simple", - "version": "0.0.1", - "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "axios": "^0.26.1", - "react": "^18.0.0", - "react-dom": "^18.0.0" - }, - "devDependencies": { - "@vitejs/plugin-react": "^2.0.0", - "vite": "^3.0.0" - } - }, - "examples/react/simple/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, - "examples/react/simple/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/simple/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/simple/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "examples/react/simple/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "examples/react/simple/node_modules/vite": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.2.tgz", - "integrity": "sha512-TAqydxW/w0U5AoL5AsD9DApTvGb2iNbGs3sN4u2VdT1GFkQVUfgUldt+t08TZgi23uIauh1TUOQJALduo9GXqw==", - "dev": true, - "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" + "examples/react/react-native/node_modules/typescript": { + "version": "4.4.4", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } + "engines": { + "node": ">=4.2.0" } }, - "examples/react/star-wars": { - "name": "@tanstack/query-example-react-star-wars", + "examples/react/react-router": { "version": "0.0.1", "dependencies": { - "@material-ui/core": "^4.9.7", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "localforage": "^1.10.0", + "match-sorter": "^6.3.1", "react": "^18.0.0", "react-dom": "^18.0.0", - "react-router": "^5.1.2", - "react-router-dom": "^5.1.2" + "react-router-dom": "^6.4.0", + "rooks": "^6.4.3", + "sort-by": "^1.2.0" }, "devDependencies": { "@vitejs/plugin-react": "^2.0.0", "vite": "^3.0.0" } }, - "examples/react/star-wars/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, + "examples/react/react-router/node_modules/react-router": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.1.tgz", + "integrity": "sha512-OJASKp5AykDWFewgWUim1vlLr7yfD4vO/h+bSgcP/ix8Md+LMHuAjovA74MQfsfhQJGGN1nHRhwS5qQQbbBt3A==", "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" + "@remix-run/router": "1.0.1" }, "engines": { - "node": ">=14.18.0" + "node": ">=14" }, "peerDependencies": { - "vite": "^3.0.0" + "react": ">=16.8" } }, - "examples/react/star-wars/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, + "examples/react/react-router/node_modules/react-router-dom": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.1.tgz", + "integrity": "sha512-MY7NJCrGNVJtGp8ODMOBHu20UaIkmwD2V3YsAOUQoCXFk7Ppdwf55RdcGyrSj+ycSL9Uiwrb3gTLYSnzcRoXww==", "dependencies": { - "sourcemap-codec": "^1.4.8" + "@remix-run/router": "1.0.1", + "react-router": "6.4.1" }, "engines": { - "node": ">=12" + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "examples/react/star-wars/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/star-wars/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], + "examples/react/rick-morty": { + "name": "@tanstack/query-example-react-rick-morty", + "version": "0.0.1", "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "@material-ui/core": "^4.9.7", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-router": "^5.1.2", + "react-router-dom": "^5.1.2" }, - "engines": { - "node": "^10 || ^12 || >=14" + "devDependencies": { + "@vitejs/plugin-react": "^2.0.0", + "vite": "^3.0.0" } }, - "examples/react/star-wars/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "examples/react/simple": { + "name": "@tanstack/query-example-react-simple", + "version": "0.0.1", + "dependencies": { + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "axios": "^0.26.1", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^2.0.0", + "vite": "^3.0.0" } }, - "examples/react/star-wars/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, + "examples/react/star-wars": { + "name": "@tanstack/query-example-react-star-wars", + "version": "0.0.1", "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" + "@material-ui/core": "^4.9.7", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-router": "^5.1.2", + "react-router-dom": "^5.1.2" }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } + "devDependencies": { + "@vitejs/plugin-react": "^2.0.0", + "vite": "^3.0.0" } }, "examples/react/suspense": { "name": "@tanstack/query-example-react-suspense", "version": "1.0.0", "dependencies": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "react": "^18.0.0", "react-dom": "^18.0.0", @@ -1722,27 +671,6 @@ "vite": "^3.0.0" } }, - "examples/react/suspense/node_modules/@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=14.18.0" - }, - "peerDependencies": { - "vite": "^3.0.0" - } - }, "examples/react/suspense/node_modules/axios": { "version": "0.21.4", "license": "MIT", @@ -1750,48 +678,6 @@ "follow-redirects": "^1.14.0" } }, - "examples/react/suspense/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "examples/react/suspense/node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "examples/react/suspense/node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "examples/react/suspense/node_modules/react-error-boundary": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-2.3.2.tgz", @@ -1807,54 +693,80 @@ "react": ">=16.13.1" } }, - "examples/react/suspense/node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true, + "examples/solid/basic-graphql-request": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "graphql": "^16.6.0", + "graphql-request": "^5.0.0", + "solid-js": "^1.5.1" + }, + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + } + }, + "examples/solid/basic-graphql-request/node_modules/graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", "engines": { - "node": ">=0.10.0" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "examples/react/suspense/node_modules/vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, + "examples/solid/basic-graphql-request/node_modules/graphql-request": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-5.0.0.tgz", + "integrity": "sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==", "dependencies": { - "esbuild": "^0.14.47", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - }, - "bin": { - "vite": "bin/vite.js" + "@graphql-typed-document-node/core": "^3.1.1", + "cross-fetch": "^3.1.5", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" }, - "engines": { - "node": "^14.18.0 || >=16.0.0" + "peerDependencies": { + "graphql": "14 - 16" + } + }, + "examples/solid/basic-typescript": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + } + }, + "examples/solid/default-query-function": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1" }, - "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*", - "terser": "^5.4.0" + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + } + }, + "examples/solid/simple": { + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1" }, - "peerDependenciesMeta": { - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "terser": { - "optional": true - } + "devDependencies": { + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" } }, "node_modules/@ampproject/remapping": { @@ -1879,27 +791,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "license": "MIT", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", + "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", + "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", + "@babel/generator": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.1", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1932,11 +845,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", "dependencies": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.19.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -1978,13 +891,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", + "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", "dependencies": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.1", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "engines": { @@ -2077,12 +990,12 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -2119,18 +1032,18 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -2147,9 +1060,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", "engines": { "node": ">=6.9.0" } @@ -2250,13 +1163,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -2275,9 +1188,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==", "bin": { "parser": "bin/babel-parser.js" }, @@ -3196,15 +2109,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", - "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.18.10" + "@babel/types": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -3774,18 +2687,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.1.tgz", + "integrity": "sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==", "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.19.0", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.19.1", + "@babel/types": "^7.19.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -3794,9 +2707,9 @@ } }, "node_modules/@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", "dependencies": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", @@ -4233,6 +3146,38 @@ "version": "0.7.5", "license": "MIT" }, + "node_modules/@esbuild/android-arm": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.9.tgz", + "integrity": "sha512-VZPy/ETF3fBG5PiinIkA0W/tlsvlEgJccyN2DzWZEl0DlVKRbu91PvY2D6Lxgluj4w9QtYHjOWjAT44C+oQ+EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.9.tgz", + "integrity": "sha512-O+NfmkfRrb3uSsTa4jE3WApidSe3N5++fyOVGP1SmMZi4A3BZELkhUUvj5hwmMuNdlpzAZ8iAPz2vmcR7DCFQA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "license": "MIT", @@ -5098,6 +4043,14 @@ "dev": true, "license": "MIT" }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/@hapi/address": { "version": "2.1.4", "license": "BSD-3-Clause" @@ -7395,6 +6348,44 @@ "node": ">= 10.18.0" } }, + "node_modules/@lerna/gitlab-client/node_modules/node-fetch": { + "version": "2.6.7", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@lerna/gitlab-client/node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@lerna/gitlab-client/node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@lerna/gitlab-client/node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/@lerna/global-options": { "version": "4.0.0", "dev": true, @@ -9252,133 +8243,45 @@ "license": "ISC" }, "node_modules/@octokit/auth-token": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", - "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", - "dev": true, - "peer": true, - "dependencies": { - "@octokit/types": "^7.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "version": "2.5.0", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/types": "^6.0.3" } }, "node_modules/@octokit/core": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", - "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", + "version": "3.6.0", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "dependencies": { - "@octokit/openapi-types": "^13.6.0" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", - "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", + "version": "6.0.12", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^6.0.3", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "dependencies": { - "@octokit/openapi-types": "^13.6.0" } }, "node_modules/@octokit/graphql": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.1.tgz", - "integrity": "sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA==", + "version": "4.8.0", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^7.0.0", + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "node_modules/@octokit/graphql/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "dependencies": { - "@octokit/openapi-types": "^13.6.0" } }, "node_modules/@octokit/openapi-types": { @@ -9423,70 +8326,64 @@ } }, "node_modules/@octokit/request": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", - "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", + "version": "5.6.3", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 14" } }, "node_modules/@octokit/request-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", - "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", + "version": "2.1.0", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^6.0.3", "deprecation": "^2.0.0", "once": "^1.4.0" - }, - "engines": { - "node": ">= 14" } }, - "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "node_modules/@octokit/request-error/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "node_modules/@octokit/request/node_modules/node-fetch": { + "version": "2.6.7", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", + "node_modules/@octokit/request/node_modules/tr46": { + "version": "0.0.3", "dev": true, - "peer": true + "license": "MIT" }, - "node_modules/@octokit/request/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "node_modules/@octokit/request/node_modules/webidl-conversions": { + "version": "3.0.1", "dev": true, - "peer": true, + "license": "BSD-2-Clause" + }, + "node_modules/@octokit/request/node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, "node_modules/@octokit/rest": { @@ -9500,77 +8397,6 @@ "@octokit/plugin-rest-endpoint-methods": "^5.12.0" } }, - "node_modules/@octokit/rest/node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dev": true, - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dev": true, - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/rest/node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, "node_modules/@octokit/types": { "version": "6.39.0", "dev": true, @@ -10551,9 +9377,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.0.tgz", - "integrity": "sha512-SCR1cxRSMNKjaVYptCzBApPDqGwa3FGdjVHc+rOToocNPHQdIYLZBfv/3f+KvYuXDkUGVIW9IAzmPNZDRL1I4A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.1.tgz", + "integrity": "sha512-eBV5rvW4dRFOU1eajN7FmYxjAIVz/mRHgUE9En9mBn6m3mulK3WTR5C3iQhL9MZ14rWAq+xOlEaCkDiW0/heOg==", "engines": { "node": ">=14" } @@ -11055,6 +9881,22 @@ "resolved": "examples/react/suspense", "link": true }, + "node_modules/@tanstack/query-example-solid-basic-graphql-request": { + "resolved": "examples/solid/basic-graphql-request", + "link": true + }, + "node_modules/@tanstack/query-example-solid-basic-typescript": { + "resolved": "examples/solid/basic-typescript", + "link": true + }, + "node_modules/@tanstack/query-example-solid-default-query-function": { + "resolved": "examples/solid/default-query-function", + "link": true + }, + "node_modules/@tanstack/query-example-solid-simple": { + "resolved": "examples/solid/simple", + "link": true + }, "node_modules/@tanstack/query-sync-storage-persister": { "resolved": "packages/query-sync-storage-persister", "link": true @@ -11090,6 +9932,10 @@ "resolved": "packages/react-query-persist-client", "link": true }, + "node_modules/@tanstack/solid-query": { + "resolved": "packages/solid-query", + "link": true + }, "node_modules/@testing-library/dom": { "version": "8.14.0", "dev": true, @@ -12087,6 +10933,48 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@vitejs/plugin-react": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.1.0.tgz", + "integrity": "sha512-am6rPyyU3LzUYne3Gd9oj9c4Rzbq5hQnuGXSMT6Gujq45Il/+bunwq3lrB7wghLkiF45ygMwft37vgJ/NE8IAA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.18.13", + "@babel/plugin-transform-react-jsx": "^7.18.10", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-jsx-self": "^7.18.6", + "@babel/plugin-transform-react-jsx-source": "^7.18.6", + "magic-string": "^0.26.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^3.0.0" + } + }, + "node_modules/@vitejs/plugin-react/node_modules/magic-string": { + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.4.tgz", + "integrity": "sha512-e5uXtVJ22aEpK9u1+eQf0fSxHeqwyV19K+uGnlROCxUhzwRip9tBsaMViK/0vC3viyPd5Gtucp3UmEp/Q2cPTQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@vitejs/plugin-react/node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@vue/compiler-core": { "version": "3.2.37", "dev": true, @@ -13213,6 +12101,39 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.34.7", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.34.7.tgz", + "integrity": "sha512-jTxBhu/MQscWdOcLfqKAY8lIiRsv1ivrMQShlePoa4G8S2cFNb93HTWN4FFdp3SpILaibygFXWU3H+aHpoGH/w==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "7.16.0", + "@babel/plugin-syntax-jsx": "^7.16.5", + "@babel/types": "^7.16.0", + "html-entities": "2.3.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.16.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + }, "node_modules/babel-plugin-macros": { "version": "2.8.0", "license": "MIT", @@ -13745,6 +12666,18 @@ "node": ">=0.10.0" } }, + "node_modules/babel-preset-solid": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.5.4.tgz", + "integrity": "sha512-pangM+KhBx8J6gRHiaRO4yD/J5gK3sydX+TIoC1TaYjxtVV78GIHRtg/HHtCAfg/iRQCJyiGR9TrN0brG8eDZA==", + "dev": true, + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.34.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/babel-runtime": { "version": "6.26.0", "license": "MIT", @@ -14171,9 +13104,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "funding": [ { "type": "opencollective", @@ -14185,10 +13118,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -14601,9 +13534,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001388", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz", - "integrity": "sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==", + "version": "1.0.30001410", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", + "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==", "funding": [ { "type": "opencollective", @@ -15778,6 +14711,40 @@ "node-fetch": "2.6.7" } }, + "node_modules/cross-fetch/node_modules/node-fetch": { + "version": "2.6.7", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/cross-fetch/node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, + "node_modules/cross-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/cross-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/cross-spawn": { "version": "6.0.5", "license": "MIT", @@ -16847,9 +15814,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.240", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.240.tgz", - "integrity": "sha512-r20dUOtZ4vUPTqAajDGonIM1uas5tf85Up+wPdtNBNvBSqGCfkpvMVvQ1T8YJzPV9/Y9g3FbUDcXb94Rafycow==" + "version": "1.4.258", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.258.tgz", + "integrity": "sha512-vutF4q0dTUXoAFI7Vbtdwen/BJVwPgj8GRg/SElOodfH7VTX+svUe62A5BG41QRQGk5HsZPB0M++KH1lAlOt0A==" }, "node_modules/elliptic": { "version": "6.5.4", @@ -16933,6 +15900,22 @@ "node": ">=6.9.0" } }, + "node_modules/enhanced-resolve-jest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve-jest/-/enhanced-resolve-jest-1.1.0.tgz", + "integrity": "sha512-GM7yVsiLIaunYkCqnGRPO4kQbT6hPSgkyOFKTseWboPMjZ2tlpQYh2ttLuE8ORkR72Wb1f9SJBbnPu0AjcTzgg==", + "dev": true, + "dependencies": { + "enhanced-resolve": "^4.1.0", + "tslib": "^1.10.0" + } + }, + "node_modules/enhanced-resolve-jest/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/enhanced-resolve/node_modules/memory-fs": { "version": "0.5.0", "license": "MIT", @@ -17102,9 +16085,9 @@ } }, "node_modules/esbuild": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.49.tgz", - "integrity": "sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.9.tgz", + "integrity": "sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==", "dev": true, "hasInstallScript": true, "bin": { @@ -17114,32 +16097,34 @@ "node": ">=12" }, "optionalDependencies": { - "esbuild-android-64": "0.14.49", - "esbuild-android-arm64": "0.14.49", - "esbuild-darwin-64": "0.14.49", - "esbuild-darwin-arm64": "0.14.49", - "esbuild-freebsd-64": "0.14.49", - "esbuild-freebsd-arm64": "0.14.49", - "esbuild-linux-32": "0.14.49", - "esbuild-linux-64": "0.14.49", - "esbuild-linux-arm": "0.14.49", - "esbuild-linux-arm64": "0.14.49", - "esbuild-linux-mips64le": "0.14.49", - "esbuild-linux-ppc64le": "0.14.49", - "esbuild-linux-riscv64": "0.14.49", - "esbuild-linux-s390x": "0.14.49", - "esbuild-netbsd-64": "0.14.49", - "esbuild-openbsd-64": "0.14.49", - "esbuild-sunos-64": "0.14.49", - "esbuild-windows-32": "0.14.49", - "esbuild-windows-64": "0.14.49", - "esbuild-windows-arm64": "0.14.49" + "@esbuild/android-arm": "0.15.9", + "@esbuild/linux-loong64": "0.15.9", + "esbuild-android-64": "0.15.9", + "esbuild-android-arm64": "0.15.9", + "esbuild-darwin-64": "0.15.9", + "esbuild-darwin-arm64": "0.15.9", + "esbuild-freebsd-64": "0.15.9", + "esbuild-freebsd-arm64": "0.15.9", + "esbuild-linux-32": "0.15.9", + "esbuild-linux-64": "0.15.9", + "esbuild-linux-arm": "0.15.9", + "esbuild-linux-arm64": "0.15.9", + "esbuild-linux-mips64le": "0.15.9", + "esbuild-linux-ppc64le": "0.15.9", + "esbuild-linux-riscv64": "0.15.9", + "esbuild-linux-s390x": "0.15.9", + "esbuild-netbsd-64": "0.15.9", + "esbuild-openbsd-64": "0.15.9", + "esbuild-sunos-64": "0.15.9", + "esbuild-windows-32": "0.15.9", + "esbuild-windows-64": "0.15.9", + "esbuild-windows-arm64": "0.15.9" } }, "node_modules/esbuild-android-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.49.tgz", - "integrity": "sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.9.tgz", + "integrity": "sha512-HQCX7FJn9T4kxZQkhPjNZC7tBWZqJvhlLHPU2SFzrQB/7nDXjmTIFpFTjt7Bd1uFpeXmuwf5h5fZm+x/hLnhbw==", "cpu": [ "x64" ], @@ -17153,9 +16138,9 @@ } }, "node_modules/esbuild-android-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.49.tgz", - "integrity": "sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.9.tgz", + "integrity": "sha512-E6zbLfqbFVCNEKircSHnPiSTsm3fCRxeIMPfrkS33tFjIAoXtwegQfVZqMGR0FlsvVxp2NEDOUz+WW48COCjSg==", "cpu": [ "arm64" ], @@ -17169,9 +16154,9 @@ } }, "node_modules/esbuild-darwin-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.49.tgz", - "integrity": "sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.9.tgz", + "integrity": "sha512-gI7dClcDN/HHVacZhTmGjl0/TWZcGuKJ0I7/xDGJwRQQn7aafZGtvagOFNmuOq+OBFPhlPv1T6JElOXb0unkSQ==", "cpu": [ "x64" ], @@ -17185,9 +16170,9 @@ } }, "node_modules/esbuild-darwin-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.49.tgz", - "integrity": "sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.9.tgz", + "integrity": "sha512-VZIMlcRN29yg/sv7DsDwN+OeufCcoTNaTl3Vnav7dL/nvsApD7uvhVRbgyMzv0zU/PP0xRhhIpTyc7lxEzHGSw==", "cpu": [ "arm64" ], @@ -17201,9 +16186,9 @@ } }, "node_modules/esbuild-freebsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.49.tgz", - "integrity": "sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.9.tgz", + "integrity": "sha512-uM4z5bTvuAXqPxrI204txhlsPIolQPWRMLenvGuCPZTnnGlCMF2QLs0Plcm26gcskhxewYo9LkkmYSS5Czrb5A==", "cpu": [ "x64" ], @@ -17217,9 +16202,9 @@ } }, "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.49.tgz", - "integrity": "sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.9.tgz", + "integrity": "sha512-HHDjT3O5gWzicGdgJ5yokZVN9K9KG05SnERwl9nBYZaCjcCgj/sX8Ps1jvoFSfNCO04JSsHSOWo4qvxFuj8FoA==", "cpu": [ "arm64" ], @@ -17233,9 +16218,9 @@ } }, "node_modules/esbuild-linux-32": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.49.tgz", - "integrity": "sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.9.tgz", + "integrity": "sha512-AQIdE8FugGt1DkcekKi5ycI46QZpGJ/wqcMr7w6YUmOmp2ohQ8eO4sKUsOxNOvYL7hGEVwkndSyszR6HpVHLFg==", "cpu": [ "ia32" ], @@ -17249,9 +16234,9 @@ } }, "node_modules/esbuild-linux-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.49.tgz", - "integrity": "sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.9.tgz", + "integrity": "sha512-4RXjae7g6Qs7StZyiYyXTZXBlfODhb1aBVAjd+ANuPmMhWthQilWo7rFHwJwL7DQu1Fjej2sODAVwLbcIVsAYQ==", "cpu": [ "x64" ], @@ -17265,9 +16250,9 @@ } }, "node_modules/esbuild-linux-arm": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.49.tgz", - "integrity": "sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.9.tgz", + "integrity": "sha512-3Zf2GVGUOI7XwChH3qrnTOSqfV1V4CAc/7zLVm4lO6JT6wbJrTgEYCCiNSzziSju+J9Jhf9YGWk/26quWPC6yQ==", "cpu": [ "arm" ], @@ -17281,9 +16266,9 @@ } }, "node_modules/esbuild-linux-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.49.tgz", - "integrity": "sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.9.tgz", + "integrity": "sha512-a+bTtxJmYmk9d+s2W4/R1SYKDDAldOKmWjWP0BnrWtDbvUBNOm++du0ysPju4mZVoEFgS1yLNW+VXnG/4FNwdQ==", "cpu": [ "arm64" ], @@ -17297,9 +16282,9 @@ } }, "node_modules/esbuild-linux-mips64le": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.49.tgz", - "integrity": "sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.9.tgz", + "integrity": "sha512-Zn9HSylDp89y+TRREMDoGrc3Z4Hs5u56ozZLQCiZAUx2+HdbbXbWdjmw3FdTJ/i7t5Cew6/Q+6kfO3KCcFGlyw==", "cpu": [ "mips64el" ], @@ -17313,9 +16298,9 @@ } }, "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.49.tgz", - "integrity": "sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.9.tgz", + "integrity": "sha512-OEiOxNAMH9ENFYqRsWUj3CWyN3V8P3ZXyfNAtX5rlCEC/ERXrCEFCJji/1F6POzsXAzxvUJrTSTCy7G6BhA6Fw==", "cpu": [ "ppc64" ], @@ -17329,9 +16314,9 @@ } }, "node_modules/esbuild-linux-riscv64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.49.tgz", - "integrity": "sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.9.tgz", + "integrity": "sha512-ukm4KsC3QRausEFjzTsOZ/qqazw0YvJsKmfoZZm9QW27OHjk2XKSQGGvx8gIEswft/Sadp03/VZvAaqv5AIwNA==", "cpu": [ "riscv64" ], @@ -17345,9 +16330,9 @@ } }, "node_modules/esbuild-linux-s390x": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.49.tgz", - "integrity": "sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.9.tgz", + "integrity": "sha512-uDOQEH55wQ6ahcIKzQr3VyjGc6Po/xblLGLoUk3fVL1qjlZAibtQr6XRfy5wPJLu/M2o0vQKLq4lyJ2r1tWKcw==", "cpu": [ "s390x" ], @@ -17361,9 +16346,9 @@ } }, "node_modules/esbuild-netbsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.49.tgz", - "integrity": "sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.9.tgz", + "integrity": "sha512-yWgxaYTQz+TqX80wXRq6xAtb7GSBAp6gqLKfOdANg9qEmAI1Bxn04IrQr0Mzm4AhxvGKoHzjHjMgXbCCSSDxcw==", "cpu": [ "x64" ], @@ -17377,9 +16362,9 @@ } }, "node_modules/esbuild-openbsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.49.tgz", - "integrity": "sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.9.tgz", + "integrity": "sha512-JmS18acQl4iSAjrEha1MfEmUMN4FcnnrtTaJ7Qg0tDCOcgpPPQRLGsZqhes0vmx8VA6IqRyScqXvaL7+Q0Uf3A==", "cpu": [ "x64" ], @@ -17393,9 +16378,9 @@ } }, "node_modules/esbuild-sunos-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.49.tgz", - "integrity": "sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.9.tgz", + "integrity": "sha512-UKynGSWpzkPmXW3D2UMOD9BZPIuRaSqphxSCwScfEE05Be3KAmvjsBhht1fLzKpiFVJb0BYMd4jEbWMyJ/z1hQ==", "cpu": [ "x64" ], @@ -17409,9 +16394,9 @@ } }, "node_modules/esbuild-windows-32": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.49.tgz", - "integrity": "sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.9.tgz", + "integrity": "sha512-aqXvu4/W9XyTVqO/hw3rNxKE1TcZiEYHPsXM9LwYmKSX9/hjvfIJzXwQBlPcJ/QOxedfoMVH0YnhhQ9Ffb0RGA==", "cpu": [ "ia32" ], @@ -17425,9 +16410,9 @@ } }, "node_modules/esbuild-windows-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.49.tgz", - "integrity": "sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.9.tgz", + "integrity": "sha512-zm7h91WUmlS4idMtjvCrEeNhlH7+TNOmqw5dJPJZrgFaxoFyqYG6CKDpdFCQXdyKpD5yvzaQBOMVTCBVKGZDEg==", "cpu": [ "x64" ], @@ -17441,9 +16426,9 @@ } }, "node_modules/esbuild-windows-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.49.tgz", - "integrity": "sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.9.tgz", + "integrity": "sha512-yQEVIv27oauAtvtuhJVfSNMztJJX47ismRS6Sv2QMVV9RM+6xjbMWuuwM2nxr5A2/gj/mu2z9YlQxiwoFRCfZA==", "cpu": [ "arm64" ], @@ -22071,21 +21056,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-extendable": { "version": "1.0.1", "license": "MIT", @@ -22250,9 +21220,8 @@ }, "node_modules/is-plain-object": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -22401,18 +21370,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/isarray": { "version": "1.0.0", "license": "MIT" @@ -28938,6 +27895,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/msw/node_modules/node-fetch": { + "version": "2.6.7", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/msw/node_modules/ora": { "version": "5.4.1", "license": "MIT", @@ -28983,6 +27958,10 @@ "node": ">=8" } }, + "node_modules/msw/node_modules/tr46": { + "version": "0.0.3", + "license": "MIT" + }, "node_modules/msw/node_modules/type-fest": { "version": "1.4.0", "license": "(MIT OR CC0-1.0)", @@ -28993,6 +27972,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/msw/node_modules/webidl-conversions": { + "version": "3.0.1", + "license": "BSD-2-Clause" + }, + "node_modules/msw/node_modules/whatwg-url": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/multicast-dns": { "version": "6.2.3", "license": "MIT", @@ -29220,41 +28211,10 @@ } }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, + "version": "2.6.0", + "license": "MIT", "engines": { "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" } }, "node_modules/node-forge": { @@ -29468,88 +28428,23 @@ } }, "node_modules/node-notifier": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", - "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", - "dev": true, - "optional": true, - "peer": true, + "version": "5.4.5", + "license": "MIT", "dependencies": { "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.5", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", "shellwords": "^0.1.1", - "uuid": "^8.3.2", - "which": "^2.0.2" - } - }, - "node_modules/node-notifier/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-notifier/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-notifier/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "uuid": "dist/bin/uuid" + "which": "^1.3.0" } }, - "node_modules/node-notifier/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, + "node_modules/node-notifier/node_modules/is-wsl": { + "version": "1.1.0", + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/node-notifier/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/node-releases": { "version": "1.1.77", "license": "MIT" @@ -35965,26 +34860,6 @@ "version": "0.0.7", "license": "ISC" }, - "node_modules/react-scripts/node_modules/node-notifier": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", - "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - } - }, - "node_modules/react-scripts/node_modules/node-notifier/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/react-scripts/node_modules/normalize-package-data": { "version": "2.5.0", "license": "BSD-2-Clause", @@ -37866,9 +36741,10 @@ } }, "node_modules/rollup": { - "version": "2.76.0", + "version": "2.78.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz", + "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==", "dev": true, - "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, @@ -38005,6 +36881,31 @@ "rollup": "^2.0.0" } }, + "node_modules/rollup-plugin-visualizer/node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/rollup-plugin-visualizer/node_modules/open": { "version": "8.4.0", "dev": true, @@ -39024,10 +37925,204 @@ "node": ">= 6" } }, + "node_modules/solid-jest": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solid-jest/-/solid-jest-0.2.0.tgz", + "integrity": "sha512-1ILtAj+z6bh1vTvaDlcT8501vmkzkVZMk2aiexJy+XWTZ+sb9B7IWedvWadIhOwwL97fiW4eMmN6SrbaHjn12A==", + "dev": true, + "dependencies": { + "@babel/preset-env": "^7.13.9", + "babel-jest": "^27.0.0", + "enhanced-resolve-jest": "^1.1.0" + }, + "peerDependencies": { + "babel-preset-solid": "^1.0.0" + } + }, "node_modules/solid-js": { - "version": "1.4.7", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.5.4.tgz", + "integrity": "sha512-+65anSHhH27htkhP5LuC912fviMIckgc7/yN+WWrKhS9Kp3dvtDNl5/m4GWX1lpCvcubjShqJjGt16HET5z5Ig==", + "dependencies": { + "csstype": "^3.1.0" + } + }, + "node_modules/solid-refresh": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.4.1.tgz", + "integrity": "sha512-v3tD/OXQcUyXLrWjPW1dXZyeWwP7/+GQNs8YTL09GBq+5FguA6IejJWUvJDrLIA4M0ho9/5zK2e9n+uy+4488g==", "dev": true, - "license": "MIT" + "dependencies": { + "@babel/generator": "^7.18.2", + "@babel/helper-module-imports": "^7.16.7", + "@babel/types": "^7.18.4" + }, + "peerDependencies": { + "solid-js": "^1.3" + } + }, + "node_modules/solid-testing-library": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solid-testing-library/-/solid-testing-library-0.3.0.tgz", + "integrity": "sha512-6NWVbySNVzyReBm2N6p3eF8bzxRZXHZTAmPix4vFWYol16QWVjNQsEUxvr+ZOutb0yuMZmNuGx3b6WIJYmjwMQ==", + "dev": true, + "dependencies": { + "@testing-library/dom": "^7.29.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "solid-js": ">=1.0.0" + } + }, + "node_modules/solid-testing-library/node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/solid-testing-library/node_modules/@testing-library/dom": { + "version": "7.31.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz", + "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.6", + "lz-string": "^1.4.4", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solid-testing-library/node_modules/@types/yargs": { + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/solid-testing-library/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/solid-testing-library/node_modules/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/solid-testing-library/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/solid-testing-library/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/solid-testing-library/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/solid-testing-library/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/solid-testing-library/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/solid-testing-library/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/solid-testing-library/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/sort-by": { "version": "1.2.0", @@ -40661,6 +39756,12 @@ "node": ">=0.4.0" } }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "dev": true + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "dev": true, @@ -40782,8 +39883,9 @@ } }, "node_modules/typescript": { - "version": "4.7.4", - "license": "Apache-2.0", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -40960,9 +40062,8 @@ }, "node_modules/universal-user-agent": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/universalify": { "version": "1.0.0", @@ -41040,9 +40141,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.6.tgz", - "integrity": "sha512-We7BqM9XFlcW94Op93uW8+2LXvGezs7QA0WY+f1H7RR1q46B06W6hZF6LbmOlpCS1HU22q/6NOGTGW5sCm7NJQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", "funding": [ { "type": "opencollective", @@ -41291,6 +40392,122 @@ "extsprintf": "^1.2.0" } }, + "node_modules/vite": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-3.1.3.tgz", + "integrity": "sha512-/3XWiktaopByM5bd8dqvHxRt5EEgRikevnnrpND0gRfNkrMrPaGGexhtLCzv15RcCMtV2CLw+BPas8YFeSG0KA==", + "dev": true, + "dependencies": { + "esbuild": "^0.15.6", + "postcss": "^8.4.16", + "resolve": "^1.22.1", + "rollup": "~2.78.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "less": "*", + "sass": "*", + "stylus": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-solid": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.3.6.tgz", + "integrity": "sha512-jzpurBMtwAqpNIp9lJU1PR0JDDAsuagojADVqcwsaCUIGYIwQ83+rYB/uZa+UGP4KvV397xo0NWCKHMkgW5x4Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "babel-preset-solid": "^1.4.6", + "merge-anything": "^5.0.2", + "solid-refresh": "^0.4.1" + }, + "peerDependencies": { + "solid-js": "^1.3.17", + "vite": "^3.0.0" + } + }, + "node_modules/vite-plugin-solid/node_modules/is-what": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.7.tgz", + "integrity": "sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==", + "dev": true, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/vite-plugin-solid/node_modules/merge-anything": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.0.4.tgz", + "integrity": "sha512-YFsDeY5A9SLXhL21Qn15wCWewRUW6wMTxQF4SuPe9bNdr1wsjiE44Rp8FQUTCtwO0WLdlKiFzhAVE5tlf857Tg==", + "dev": true, + "dependencies": { + "is-what": "^4.1.7", + "ts-toolbelt": "^9.6.0" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/vite/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/vlq": { "version": "1.0.1", "license": "MIT" @@ -42950,10 +42167,10 @@ }, "packages/query-async-storage-persister": { "name": "@tanstack/query-async-storage-persister", - "version": "4.3.8", + "version": "4.3.9", "license": "MIT", "dependencies": { - "@tanstack/react-query-persist-client": "4.3.8" + "@tanstack/react-query-persist-client": "4.3.9" }, "funding": { "type": "github", @@ -43025,10 +42242,10 @@ }, "packages/query-sync-storage-persister": { "name": "@tanstack/query-sync-storage-persister", - "version": "4.3.8", + "version": "4.3.9", "license": "MIT", "dependencies": { - "@tanstack/react-query-persist-client": "4.3.8" + "@tanstack/react-query-persist-client": "4.3.9" }, "funding": { "type": "github", @@ -43037,7 +42254,7 @@ }, "packages/react-query": { "name": "@tanstack/react-query", - "version": "4.3.8", + "version": "4.3.9", "license": "MIT", "dependencies": { "@tanstack/query-core": "4.3.8", @@ -43069,7 +42286,7 @@ }, "packages/react-query-devtools": { "name": "@tanstack/react-query-devtools", - "version": "4.3.8", + "version": "4.3.9", "license": "MIT", "dependencies": { "@tanstack/match-sorter-utils": "^8.1.1", @@ -43083,14 +42300,14 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-query": "4.3.8", + "@tanstack/react-query": "4.3.9", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "packages/react-query-persist-client": { "name": "@tanstack/react-query-persist-client", - "version": "4.3.8", + "version": "4.3.9", "license": "MIT", "funding": { "type": "github", @@ -43098,7 +42315,35 @@ }, "peerDependencies": { "@tanstack/query-core": "4.3.8", - "@tanstack/react-query": "4.3.8" + "@tanstack/react-query": "4.3.9" + } + }, + "packages/solid-query": { + "name": "@tanstack/solid-query", + "version": "4.3.9", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "4.3.9" + }, + "devDependencies": { + "@types/jscodeshift": "^0.11.3", + "jscodeshift": "^0.13.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "solid-js": "^1.5.4" + } + }, + "packages/solid-query/node_modules/@tanstack/query-core": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.3.9.tgz", + "integrity": "sha512-GFtngk07PTj3WX1P0sN2hnb/z+Y+CnGtwJv2gdDepZ5M87vgaTb2y9qqd6HDpMswTAmEqjBbrNp7X/LCBZkY+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" } } }, @@ -43117,23 +42362,25 @@ } }, "@babel/compat-data": { - "version": "7.18.8" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.1.tgz", + "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==" }, "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.1.tgz", + "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==", "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", + "@babel/generator": "^7.19.0", + "@babel/helper-compilation-targets": "^7.19.1", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helpers": "^7.19.0", + "@babel/parser": "^7.19.1", "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -43150,11 +42397,11 @@ } }, "@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", "requires": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.19.0", "@jridgewell/gen-mapping": "^0.3.2", "jsesc": "^2.5.1" }, @@ -43183,13 +42430,13 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz", + "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==", "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.19.1", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", "semver": "^6.3.0" }, "dependencies": { @@ -43247,12 +42494,12 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" } }, "@babel/helper-hoist-variables": { @@ -43274,18 +42521,18 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", "@babel/helper-simple-access": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/helper-optimise-call-expression": { @@ -43295,9 +42542,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==" + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" }, "@babel/helper-remap-async-to-generator": { "version": "7.18.6", @@ -43357,13 +42604,13 @@ } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" } }, "@babel/highlight": { @@ -43375,9 +42622,9 @@ } }, "@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==" + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.1.tgz", + "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -43861,15 +43108,15 @@ } }, "@babel/plugin-transform-react-jsx": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz", - "integrity": "sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz", + "integrity": "sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==", "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/types": "^7.18.10" + "@babel/types": "^7.19.0" } }, "@babel/plugin-transform-react-jsx-development": { @@ -44219,26 +43466,26 @@ } }, "@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.1.tgz", + "integrity": "sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==", "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.19.0", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.19.1", + "@babel/types": "^7.19.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", "requires": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", @@ -44496,6 +43743,20 @@ "@emotion/unitless": { "version": "0.7.5" }, + "@esbuild/android-arm": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.9.tgz", + "integrity": "sha512-VZPy/ETF3fBG5PiinIkA0W/tlsvlEgJccyN2DzWZEl0DlVKRbu91PvY2D6Lxgluj4w9QtYHjOWjAT44C+oQ+EQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.9.tgz", + "integrity": "sha512-O+NfmkfRrb3uSsTa4jE3WApidSe3N5++fyOVGP1SmMZi4A3BZELkhUUvj5hwmMuNdlpzAZ8iAPz2vmcR7DCFQA==", + "dev": true, + "optional": true + }, "@eslint/eslintrc": { "version": "0.4.3", "requires": { @@ -45089,6 +44350,12 @@ "version": "1.1.3", "dev": true }, + "@graphql-typed-document-node/core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "requires": {} + }, "@hapi/address": { "version": "2.1.4" }, @@ -46626,6 +45893,33 @@ "node-fetch": "^2.6.1", "npmlog": "^4.1.2", "whatwg-url": "^8.4.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "5.0.0", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, + "tr46": { + "version": "0.0.3", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "dev": true + } } }, "@lerna/global-options": { @@ -47814,129 +47108,41 @@ } }, "@octokit/auth-token": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", - "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", + "version": "2.5.0", "dev": true, - "peer": true, "requires": { - "@octokit/types": "^7.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "requires": { - "@octokit/openapi-types": "^13.6.0" - } - } + "@octokit/types": "^6.0.3" } }, "@octokit/core": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", - "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", + "version": "3.6.0", "dev": true, - "peer": true, "requires": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "requires": { - "@octokit/openapi-types": "^13.6.0" - } - } } }, "@octokit/endpoint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", - "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", + "version": "6.0.12", "dev": true, - "peer": true, "requires": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^6.0.3", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "requires": { - "@octokit/openapi-types": "^13.6.0" - } - } } }, "@octokit/graphql": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.1.tgz", - "integrity": "sha512-sxmnewSwAixkP1TrLdE6yRG53eEhHhDTYUykUwdV9x8f91WcbhunIHk9x1PZLALdBZKRPUO2HRcm4kezZ79HoA==", + "version": "4.8.0", "dev": true, - "peer": true, "requires": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^7.0.0", + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "requires": { - "@octokit/openapi-types": "^13.6.0" - } - } } }, "@octokit/openapi-types": { @@ -47968,68 +47174,49 @@ } }, "@octokit/request": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", - "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", + "version": "5.6.3", "dev": true, - "peer": true, "requires": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" }, "dependencies": { - "@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", + "node-fetch": { + "version": "2.6.7", "dev": true, - "peer": true + "requires": { + "whatwg-url": "^5.0.0" + } }, - "@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", + "tr46": { + "version": "0.0.3", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", "dev": true, - "peer": true, "requires": { - "@octokit/openapi-types": "^13.6.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } } } }, "@octokit/request-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", - "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", + "version": "2.1.0", "dev": true, - "peer": true, "requires": { - "@octokit/types": "^7.0.0", + "@octokit/types": "^6.0.3", "deprecation": "^2.0.0", "once": "^1.4.0" - }, - "dependencies": { - "@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==", - "dev": true, - "peer": true - }, - "@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dev": true, - "peer": true, - "requires": { - "@octokit/openapi-types": "^13.6.0" - } - } } }, "@octokit/rest": { @@ -48040,79 +47227,6 @@ "@octokit/plugin-paginate-rest": "^2.16.8", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - }, - "dependencies": { - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dev": true, - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dev": true, - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - } } }, "@octokit/types": { @@ -48780,9 +47894,9 @@ } }, "@remix-run/router": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.0.tgz", - "integrity": "sha512-SCR1cxRSMNKjaVYptCzBApPDqGwa3FGdjVHc+rOToocNPHQdIYLZBfv/3f+KvYuXDkUGVIW9IAzmPNZDRL1I4A==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.1.tgz", + "integrity": "sha512-eBV5rvW4dRFOU1eajN7FmYxjAIVz/mRHgUE9En9mBn6m3mulK3WTR5C3iQhL9MZ14rWAq+xOlEaCkDiW0/heOg==" }, "@rescripts/cli": { "version": "0.0.11", @@ -49054,7 +48168,7 @@ "@tanstack/query-async-storage-persister": { "version": "file:packages/query-async-storage-persister", "requires": { - "@tanstack/react-query-persist-client": "4.3.8" + "@tanstack/react-query-persist-client": "4.3.9" } }, "@tanstack/query-broadcast-client-experimental": { @@ -49100,8 +48214,8 @@ "@tanstack/query-example-react-basic": { "version": "file:examples/react/basic", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "axios": "^0.21.1", "react": "^18.0.0", @@ -49109,147 +48223,25 @@ "vite": "^3.0.0" }, "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, "axios": { "version": "0.21.4", "requires": { "follow-redirects": "^1.14.0" } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } } } }, "@tanstack/query-example-react-basic-graphql-request": { "version": "file:examples/react/basic-graphql-request", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "graphql": "^15.3.0", "graphql-request": "^3.1.0", "react": "^18.0.0", "react-dom": "^18.0.0", "vite": "^3.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } - } } }, "@tanstack/query-example-react-basic-typescript": { @@ -49258,10 +48250,10 @@ "@rescripts/cli": "^0.0.11", "@rescripts/rescript-use-babel-config": "^0.0.8", "@rescripts/rescript-use-eslint-config": "^0.0.9", - "@tanstack/query-sync-storage-persister": "4.3.8", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "@tanstack/react-query-persist-client": "4.3.8", + "@tanstack/query-sync-storage-persister": "4.3.9", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "@tanstack/react-query-persist-client": "4.3.9", "@types/react": "^17.0.3", "@types/react-dom": "^17.0.3", "axios": "^0.26.1", @@ -49314,82 +48306,20 @@ "@tanstack/query-example-react-default-query-function": { "version": "file:examples/react/default-query-function", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "axios": "^0.26.1", "react": "^18.0.0", "react-dom": "^18.0.0", "vite": "^3.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } - } } }, "@tanstack/query-example-react-load-more-infinite-scroll": { "version": "file:examples/react/load-more-infinite-scroll", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -49409,8 +48339,8 @@ "@tanstack/query-example-react-nextjs": { "version": "file:examples/react/nextjs", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "ky": "^0.23.0", "ky-universal": "^0.8.2", "next": "12.2.2", @@ -49423,11 +48353,11 @@ "@tanstack/query-example-react-offline": { "version": "file:examples/react/offline", "requires": { - "@tanstack/query-sync-storage-persister": "4.3.8", + "@tanstack/query-sync-storage-persister": "4.3.9", "@tanstack/react-location": "^3.7.0", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", - "@tanstack/react-query-persist-client": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", + "@tanstack/react-query-persist-client": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "ky": "^0.30.0", "msw": "^0.39.2", @@ -49437,76 +48367,16 @@ "vite": "^3.0.0" }, "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, "ky": { "version": "0.30.0" - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } } } }, "@tanstack/query-example-react-optimistic-updates-typescript": { "version": "file:examples/react/optimistic-updates-typescript", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@types/node": "14.14.14", "@types/react": "^18.0.15", "axios": "^0.21.1", @@ -49531,8 +48401,8 @@ "@tanstack/query-example-react-pagination": { "version": "file:examples/react/pagination", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -49551,81 +48421,19 @@ "@tanstack/query-example-react-playground": { "version": "file:examples/react/playground", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "vite": "^3.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } - } } }, "@tanstack/query-example-react-prefetching": { "version": "file:examples/react/prefetching", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -49650,8 +48458,8 @@ "@react-native-community/netinfo": "6.0.2", "@react-navigation/native": "^6.0.2", "@react-navigation/stack": "^6.0.2", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@types/react-native": "~0.64.3", "babel-plugin-module-resolver": "^4.1.0", "eslint": "^7.32.0", @@ -49711,8 +48519,8 @@ "@tanstack/query-example-react-refetch-interval": { "version": "file:examples/react/auto-refetching", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "axios": "^0.21.1", "isomorphic-unfetch": "3.0.0", "next": "12.2.2", @@ -49732,83 +48540,21 @@ "version": "file:examples/react/rick-morty", "requires": { "@material-ui/core": "^4.9.7", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", "vite": "^3.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } - } } }, "@tanstack/query-example-react-router": { "version": "file:examples/react/react-router", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "localforage": "^1.10.0", "match-sorter": "^6.3.1", @@ -49820,110 +48566,21 @@ "vite": "^3.0.0" }, "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.1.tgz", - "integrity": "sha512-uINzNHmjrbunlFtyVkST6lY1ewSfz/XwLufG0PIqvLGnpk2nOIOa/1CACTDNcKi1/RwaCzJLmsXwm1NsUVV/NA==", - "dev": true, - "requires": { - "@babel/core": "^7.18.10", - "@babel/plugin-transform-react-jsx": "^7.18.10", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", - "dev": true, - "optional": true, - "peer": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, "react-router": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.0.tgz", - "integrity": "sha512-B+5bEXFlgR1XUdHYR6P94g299SjrfCBMmEDJNcFbpAyRH1j1748yt9NdDhW3++nw1lk3zQJ6aOO66zUx3KlTZg==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.4.1.tgz", + "integrity": "sha512-OJASKp5AykDWFewgWUim1vlLr7yfD4vO/h+bSgcP/ix8Md+LMHuAjovA74MQfsfhQJGGN1nHRhwS5qQQbbBt3A==", "requires": { - "@remix-run/router": "1.0.0" + "@remix-run/router": "1.0.1" } }, "react-router-dom": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.0.tgz", - "integrity": "sha512-4Aw1xmXKeleYYQ3x0Lcl2undHR6yMjXZjd9DKZd53SGOYqirrUThyUb0wwAX5VZAyvSuzjNJmZlJ3rR9+/vzqg==", - "requires": { - "react-router": "6.4.0" - } - }, - "terser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", - "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - } - }, - "vite": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.9.tgz", - "integrity": "sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==", - "dev": true, + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.4.1.tgz", + "integrity": "sha512-MY7NJCrGNVJtGp8ODMOBHu20UaIkmwD2V3YsAOUQoCXFk7Ppdwf55RdcGyrSj+ycSL9Uiwrb3gTLYSnzcRoXww==", "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.16", - "resolve": "^1.22.1", - "rollup": ">=2.75.6 <2.77.0 || ~2.77.0" + "@remix-run/router": "1.0.1", + "react-router": "6.4.1" } } } @@ -49931,158 +48588,34 @@ "@tanstack/query-example-react-simple": { "version": "file:examples/react/simple", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "axios": "^0.26.1", "react": "^18.0.0", "react-dom": "^18.0.0", "vite": "^3.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.2.tgz", - "integrity": "sha512-TAqydxW/w0U5AoL5AsD9DApTvGb2iNbGs3sN4u2VdT1GFkQVUfgUldt+t08TZgi23uIauh1TUOQJALduo9GXqw==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } - } } }, "@tanstack/query-example-react-star-wars": { "version": "file:examples/react/star-wars", "requires": { "@material-ui/core": "^4.9.7", - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-router": "^5.1.2", "react-router-dom": "^5.1.2", "vite": "^3.0.0" - }, - "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, - "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" - } - } } }, "@tanstack/query-example-react-suspense": { "version": "file:examples/react/suspense", "requires": { - "@tanstack/react-query": "4.3.8", - "@tanstack/react-query-devtools": "4.3.8", + "@tanstack/react-query": "4.3.9", + "@tanstack/react-query-devtools": "4.3.9", "@vitejs/plugin-react": "^2.0.0", "axios": "^0.21.1", "react": "^18.0.0", @@ -50091,53 +48624,12 @@ "vite": "^3.0.0" }, "dependencies": { - "@vitejs/plugin-react": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.0.0.tgz", - "integrity": "sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==", - "dev": true, - "requires": { - "@babel/core": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-jsx-self": "^7.18.6", - "@babel/plugin-transform-react-jsx-source": "^7.18.6", - "magic-string": "^0.26.2", - "react-refresh": "^0.14.0" - } - }, "axios": { "version": "0.21.4", "requires": { "follow-redirects": "^1.14.0" } }, - "magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "dev": true, - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, "react-error-boundary": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-2.3.2.tgz", @@ -50145,32 +48637,73 @@ "requires": { "@babel/runtime": "^7.11.2" } + } + } + }, + "@tanstack/query-example-solid-basic-graphql-request": { + "version": "file:examples/solid/basic-graphql-request", + "requires": { + "@tanstack/solid-query": "^4.3.9", + "graphql": "^16.6.0", + "graphql-request": "^5.0.0", + "solid-js": "^1.5.1", + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + }, + "dependencies": { + "graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==" }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "dev": true - }, - "vite": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-3.0.4.tgz", - "integrity": "sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==", - "dev": true, + "graphql-request": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-5.0.0.tgz", + "integrity": "sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==", "requires": { - "esbuild": "^0.14.47", - "fsevents": "~2.3.2", - "postcss": "^8.4.14", - "resolve": "^1.22.1", - "rollup": "^2.75.6" + "@graphql-typed-document-node/core": "^3.1.1", + "cross-fetch": "^3.1.5", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" } } } }, + "@tanstack/query-example-solid-basic-typescript": { + "version": "file:examples/solid/basic-typescript", + "requires": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1", + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + } + }, + "@tanstack/query-example-solid-default-query-function": { + "version": "file:examples/solid/default-query-function", + "requires": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1", + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + } + }, + "@tanstack/query-example-solid-simple": { + "version": "file:examples/solid/simple", + "requires": { + "@tanstack/solid-query": "^4.3.9", + "solid-js": "^1.5.1", + "typescript": "^4.8.2", + "vite": "^3.0.9", + "vite-plugin-solid": "^2.3.0" + } + }, "@tanstack/query-sync-storage-persister": { "version": "file:packages/query-sync-storage-persister", "requires": { - "@tanstack/react-query-persist-client": "4.3.8" + "@tanstack/react-query-persist-client": "4.3.9" } }, "@tanstack/react-location": { @@ -50203,6 +48736,21 @@ "version": "file:packages/react-query-persist-client", "requires": {} }, + "@tanstack/solid-query": { + "version": "file:packages/solid-query", + "requires": { + "@tanstack/query-core": "4.3.9", + "@types/jscodeshift": "^0.11.3", + "jscodeshift": "^0.13.1" + }, + "dependencies": { + "@tanstack/query-core": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.3.9.tgz", + "integrity": "sha512-GFtngk07PTj3WX1P0sN2hnb/z+Y+CnGtwJv2gdDepZ5M87vgaTb2y9qqd6HDpMswTAmEqjBbrNp7X/LCBZkY+A==" + } + } + }, "@testing-library/dom": { "version": "8.14.0", "dev": true, @@ -50857,6 +49405,38 @@ } } }, + "@vitejs/plugin-react": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-2.1.0.tgz", + "integrity": "sha512-am6rPyyU3LzUYne3Gd9oj9c4Rzbq5hQnuGXSMT6Gujq45Il/+bunwq3lrB7wghLkiF45ygMwft37vgJ/NE8IAA==", + "dev": true, + "requires": { + "@babel/core": "^7.18.13", + "@babel/plugin-transform-react-jsx": "^7.18.10", + "@babel/plugin-transform-react-jsx-development": "^7.18.6", + "@babel/plugin-transform-react-jsx-self": "^7.18.6", + "@babel/plugin-transform-react-jsx-source": "^7.18.6", + "magic-string": "^0.26.2", + "react-refresh": "^0.14.0" + }, + "dependencies": { + "magic-string": { + "version": "0.26.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.4.tgz", + "integrity": "sha512-e5uXtVJ22aEpK9u1+eQf0fSxHeqwyV19K+uGnlROCxUhzwRip9tBsaMViK/0vC3viyPd5Gtucp3UmEp/Q2cPTQ==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true + } + } + }, "@vue/compiler-core": { "version": "3.2.37", "dev": true, @@ -51636,6 +50216,35 @@ "@types/babel__traverse": "^7.0.6" } }, + "babel-plugin-jsx-dom-expressions": { + "version": "0.34.7", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.34.7.tgz", + "integrity": "sha512-jTxBhu/MQscWdOcLfqKAY8lIiRsv1ivrMQShlePoa4G8S2cFNb93HTWN4FFdp3SpILaibygFXWU3H+aHpoGH/w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "7.16.0", + "@babel/plugin-syntax-jsx": "^7.16.5", + "@babel/types": "^7.16.0", + "html-entities": "2.3.2" + }, + "dependencies": { + "@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, + "requires": { + "@babel/types": "^7.16.0" + } + }, + "html-entities": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz", + "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==", + "dev": true + } + } + }, "babel-plugin-macros": { "version": "2.8.0", "requires": { @@ -52032,6 +50641,15 @@ } } }, + "babel-preset-solid": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.5.4.tgz", + "integrity": "sha512-pangM+KhBx8J6gRHiaRO4yD/J5gK3sydX+TIoC1TaYjxtVV78GIHRtg/HHtCAfg/iRQCJyiGR9TrN0brG8eDZA==", + "dev": true, + "requires": { + "babel-plugin-jsx-dom-expressions": "^0.34.5" + } + }, "babel-runtime": { "version": "6.26.0", "requires": { @@ -52337,14 +50955,14 @@ } }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "dependencies": { "node-releases": { @@ -52608,9 +51226,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001388", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001388.tgz", - "integrity": "sha512-znVbq4OUjqgLxMxoNX2ZeeLR0d7lcDiE5uJ4eUiWdml1J1EkxbnQq6opT9jb9SMfJxB0XA16/ziHwni4u1I3GQ==" + "version": "1.0.30001410", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz", + "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==" }, "capture-exit": { "version": "2.0.0", @@ -53392,6 +52010,27 @@ "version": "3.1.5", "requires": { "node-fetch": "2.6.7" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3" + }, + "webidl-conversions": { + "version": "3.0.1" + }, + "whatwg-url": { + "version": "5.0.0", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } } }, "cross-spawn": { @@ -54118,9 +52757,9 @@ "version": "1.1.1" }, "electron-to-chromium": { - "version": "1.4.240", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.240.tgz", - "integrity": "sha512-r20dUOtZ4vUPTqAajDGonIM1uas5tf85Up+wPdtNBNvBSqGCfkpvMVvQ1T8YJzPV9/Y9g3FbUDcXb94Rafycow==" + "version": "1.4.258", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.258.tgz", + "integrity": "sha512-vutF4q0dTUXoAFI7Vbtdwen/BJVwPgj8GRg/SElOodfH7VTX+svUe62A5BG41QRQGk5HsZPB0M++KH1lAlOt0A==" }, "elliptic": { "version": "6.5.4", @@ -54208,6 +52847,24 @@ } } }, + "enhanced-resolve-jest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve-jest/-/enhanced-resolve-jest-1.1.0.tgz", + "integrity": "sha512-GM7yVsiLIaunYkCqnGRPO4kQbT6hPSgkyOFKTseWboPMjZ2tlpQYh2ttLuE8ORkR72Wb1f9SJBbnPu0AjcTzgg==", + "dev": true, + "requires": { + "enhanced-resolve": "^4.1.0", + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, "enquirer": { "version": "2.3.6", "requires": { @@ -54300,170 +52957,172 @@ } }, "esbuild": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.49.tgz", - "integrity": "sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==", - "dev": true, - "requires": { - "esbuild-android-64": "0.14.49", - "esbuild-android-arm64": "0.14.49", - "esbuild-darwin-64": "0.14.49", - "esbuild-darwin-arm64": "0.14.49", - "esbuild-freebsd-64": "0.14.49", - "esbuild-freebsd-arm64": "0.14.49", - "esbuild-linux-32": "0.14.49", - "esbuild-linux-64": "0.14.49", - "esbuild-linux-arm": "0.14.49", - "esbuild-linux-arm64": "0.14.49", - "esbuild-linux-mips64le": "0.14.49", - "esbuild-linux-ppc64le": "0.14.49", - "esbuild-linux-riscv64": "0.14.49", - "esbuild-linux-s390x": "0.14.49", - "esbuild-netbsd-64": "0.14.49", - "esbuild-openbsd-64": "0.14.49", - "esbuild-sunos-64": "0.14.49", - "esbuild-windows-32": "0.14.49", - "esbuild-windows-64": "0.14.49", - "esbuild-windows-arm64": "0.14.49" + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.9.tgz", + "integrity": "sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.15.9", + "@esbuild/linux-loong64": "0.15.9", + "esbuild-android-64": "0.15.9", + "esbuild-android-arm64": "0.15.9", + "esbuild-darwin-64": "0.15.9", + "esbuild-darwin-arm64": "0.15.9", + "esbuild-freebsd-64": "0.15.9", + "esbuild-freebsd-arm64": "0.15.9", + "esbuild-linux-32": "0.15.9", + "esbuild-linux-64": "0.15.9", + "esbuild-linux-arm": "0.15.9", + "esbuild-linux-arm64": "0.15.9", + "esbuild-linux-mips64le": "0.15.9", + "esbuild-linux-ppc64le": "0.15.9", + "esbuild-linux-riscv64": "0.15.9", + "esbuild-linux-s390x": "0.15.9", + "esbuild-netbsd-64": "0.15.9", + "esbuild-openbsd-64": "0.15.9", + "esbuild-sunos-64": "0.15.9", + "esbuild-windows-32": "0.15.9", + "esbuild-windows-64": "0.15.9", + "esbuild-windows-arm64": "0.15.9" } }, "esbuild-android-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.49.tgz", - "integrity": "sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.9.tgz", + "integrity": "sha512-HQCX7FJn9T4kxZQkhPjNZC7tBWZqJvhlLHPU2SFzrQB/7nDXjmTIFpFTjt7Bd1uFpeXmuwf5h5fZm+x/hLnhbw==", "dev": true, "optional": true }, "esbuild-android-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.49.tgz", - "integrity": "sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.9.tgz", + "integrity": "sha512-E6zbLfqbFVCNEKircSHnPiSTsm3fCRxeIMPfrkS33tFjIAoXtwegQfVZqMGR0FlsvVxp2NEDOUz+WW48COCjSg==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.49.tgz", - "integrity": "sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.9.tgz", + "integrity": "sha512-gI7dClcDN/HHVacZhTmGjl0/TWZcGuKJ0I7/xDGJwRQQn7aafZGtvagOFNmuOq+OBFPhlPv1T6JElOXb0unkSQ==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.49.tgz", - "integrity": "sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.9.tgz", + "integrity": "sha512-VZIMlcRN29yg/sv7DsDwN+OeufCcoTNaTl3Vnav7dL/nvsApD7uvhVRbgyMzv0zU/PP0xRhhIpTyc7lxEzHGSw==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.49.tgz", - "integrity": "sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.9.tgz", + "integrity": "sha512-uM4z5bTvuAXqPxrI204txhlsPIolQPWRMLenvGuCPZTnnGlCMF2QLs0Plcm26gcskhxewYo9LkkmYSS5Czrb5A==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.49.tgz", - "integrity": "sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.9.tgz", + "integrity": "sha512-HHDjT3O5gWzicGdgJ5yokZVN9K9KG05SnERwl9nBYZaCjcCgj/sX8Ps1jvoFSfNCO04JSsHSOWo4qvxFuj8FoA==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.49.tgz", - "integrity": "sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.9.tgz", + "integrity": "sha512-AQIdE8FugGt1DkcekKi5ycI46QZpGJ/wqcMr7w6YUmOmp2ohQ8eO4sKUsOxNOvYL7hGEVwkndSyszR6HpVHLFg==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.49.tgz", - "integrity": "sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.9.tgz", + "integrity": "sha512-4RXjae7g6Qs7StZyiYyXTZXBlfODhb1aBVAjd+ANuPmMhWthQilWo7rFHwJwL7DQu1Fjej2sODAVwLbcIVsAYQ==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.49.tgz", - "integrity": "sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.9.tgz", + "integrity": "sha512-3Zf2GVGUOI7XwChH3qrnTOSqfV1V4CAc/7zLVm4lO6JT6wbJrTgEYCCiNSzziSju+J9Jhf9YGWk/26quWPC6yQ==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.49.tgz", - "integrity": "sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.9.tgz", + "integrity": "sha512-a+bTtxJmYmk9d+s2W4/R1SYKDDAldOKmWjWP0BnrWtDbvUBNOm++du0ysPju4mZVoEFgS1yLNW+VXnG/4FNwdQ==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.49.tgz", - "integrity": "sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.9.tgz", + "integrity": "sha512-Zn9HSylDp89y+TRREMDoGrc3Z4Hs5u56ozZLQCiZAUx2+HdbbXbWdjmw3FdTJ/i7t5Cew6/Q+6kfO3KCcFGlyw==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.49.tgz", - "integrity": "sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.9.tgz", + "integrity": "sha512-OEiOxNAMH9ENFYqRsWUj3CWyN3V8P3ZXyfNAtX5rlCEC/ERXrCEFCJji/1F6POzsXAzxvUJrTSTCy7G6BhA6Fw==", "dev": true, "optional": true }, "esbuild-linux-riscv64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.49.tgz", - "integrity": "sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.9.tgz", + "integrity": "sha512-ukm4KsC3QRausEFjzTsOZ/qqazw0YvJsKmfoZZm9QW27OHjk2XKSQGGvx8gIEswft/Sadp03/VZvAaqv5AIwNA==", "dev": true, "optional": true }, "esbuild-linux-s390x": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.49.tgz", - "integrity": "sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.9.tgz", + "integrity": "sha512-uDOQEH55wQ6ahcIKzQr3VyjGc6Po/xblLGLoUk3fVL1qjlZAibtQr6XRfy5wPJLu/M2o0vQKLq4lyJ2r1tWKcw==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.49.tgz", - "integrity": "sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.9.tgz", + "integrity": "sha512-yWgxaYTQz+TqX80wXRq6xAtb7GSBAp6gqLKfOdANg9qEmAI1Bxn04IrQr0Mzm4AhxvGKoHzjHjMgXbCCSSDxcw==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.49.tgz", - "integrity": "sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.9.tgz", + "integrity": "sha512-JmS18acQl4iSAjrEha1MfEmUMN4FcnnrtTaJ7Qg0tDCOcgpPPQRLGsZqhes0vmx8VA6IqRyScqXvaL7+Q0Uf3A==", "dev": true, "optional": true }, "esbuild-sunos-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.49.tgz", - "integrity": "sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.9.tgz", + "integrity": "sha512-UKynGSWpzkPmXW3D2UMOD9BZPIuRaSqphxSCwScfEE05Be3KAmvjsBhht1fLzKpiFVJb0BYMd4jEbWMyJ/z1hQ==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.49.tgz", - "integrity": "sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.9.tgz", + "integrity": "sha512-aqXvu4/W9XyTVqO/hw3rNxKE1TcZiEYHPsXM9LwYmKSX9/hjvfIJzXwQBlPcJ/QOxedfoMVH0YnhhQ9Ffb0RGA==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.49.tgz", - "integrity": "sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.9.tgz", + "integrity": "sha512-zm7h91WUmlS4idMtjvCrEeNhlH7+TNOmqw5dJPJZrgFaxoFyqYG6CKDpdFCQXdyKpD5yvzaQBOMVTCBVKGZDEg==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.14.49", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.49.tgz", - "integrity": "sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==", + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.9.tgz", + "integrity": "sha512-yQEVIv27oauAtvtuhJVfSNMztJJX47ismRS6Sv2QMVV9RM+6xjbMWuuwM2nxr5A2/gj/mu2z9YlQxiwoFRCfZA==", "dev": true, "optional": true }, @@ -57437,12 +56096,6 @@ "is-directory": { "version": "0.3.1" }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, "is-extendable": { "version": "1.0.1", "requires": { @@ -57537,8 +56190,6 @@ }, "is-plain-object": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, "is-potential-custom-element-name": { @@ -57624,15 +56275,6 @@ "is-windows": { "version": "1.0.2" }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, "isarray": { "version": "1.0.0" }, @@ -62153,6 +60795,12 @@ "is-unicode-supported": "^0.1.0" } }, + "node-fetch": { + "version": "2.6.7", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "ora": { "version": "5.4.1", "requires": { @@ -62182,8 +60830,21 @@ "has-flag": "^4.0.0" } }, + "tr46": { + "version": "0.0.3" + }, "type-fest": { "version": "1.4.0" + }, + "webidl-conversions": { + "version": "3.0.1" + }, + "whatwg-url": { + "version": "5.0.0", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } }, @@ -62336,33 +60997,7 @@ } }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "requires": { - "whatwg-url": "^5.0.0" - }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - } - } + "version": "2.6.0" }, "node-forge": { "version": "0.10.0" @@ -62523,69 +61158,17 @@ } }, "node-notifier": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-10.0.1.tgz", - "integrity": "sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==", - "dev": true, - "optional": true, - "peer": true, + "version": "5.4.5", "requires": { "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.5", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", "shellwords": "^0.1.1", - "uuid": "^8.3.2", - "which": "^2.0.2" + "which": "^1.3.0" }, "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "optional": true, - "peer": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "optional": true, - "peer": true + "is-wsl": { + "version": "1.1.0" } } }, @@ -67060,25 +65643,6 @@ "mute-stream": { "version": "0.0.7" }, - "node-notifier": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.5.tgz", - "integrity": "sha512-tVbHs7DyTLtzOiN78izLA85zRqB9NvEXkAf014Vx3jtSvn/xBl6bR8ZYifj+dFcFrKI21huSQgJZ6ZtL3B4HfQ==", - "requires": { - "growly": "^1.3.0", - "is-wsl": "^1.1.0", - "semver": "^5.5.0", - "shellwords": "^0.1.1", - "which": "^1.3.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, "normalize-package-data": { "version": "2.5.0", "requires": { @@ -68356,7 +66920,9 @@ } }, "rollup": { - "version": "2.76.0", + "version": "2.78.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz", + "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -68437,6 +67003,17 @@ "yargs": "^17.3.1" }, "dependencies": { + "is-docker": { + "version": "2.2.1", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, "open": { "version": "8.4.0", "dev": true, @@ -69151,9 +67728,161 @@ "socks": "^2.3.3" } }, + "solid-jest": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solid-jest/-/solid-jest-0.2.0.tgz", + "integrity": "sha512-1ILtAj+z6bh1vTvaDlcT8501vmkzkVZMk2aiexJy+XWTZ+sb9B7IWedvWadIhOwwL97fiW4eMmN6SrbaHjn12A==", + "dev": true, + "requires": { + "@babel/preset-env": "^7.13.9", + "babel-jest": "^27.0.0", + "enhanced-resolve-jest": "^1.1.0" + } + }, "solid-js": { - "version": "1.4.7", - "dev": true + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.5.4.tgz", + "integrity": "sha512-+65anSHhH27htkhP5LuC912fviMIckgc7/yN+WWrKhS9Kp3dvtDNl5/m4GWX1lpCvcubjShqJjGt16HET5z5Ig==", + "requires": { + "csstype": "^3.1.0" + } + }, + "solid-refresh": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.4.1.tgz", + "integrity": "sha512-v3tD/OXQcUyXLrWjPW1dXZyeWwP7/+GQNs8YTL09GBq+5FguA6IejJWUvJDrLIA4M0ho9/5zK2e9n+uy+4488g==", + "dev": true, + "requires": { + "@babel/generator": "^7.18.2", + "@babel/helper-module-imports": "^7.16.7", + "@babel/types": "^7.18.4" + } + }, + "solid-testing-library": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/solid-testing-library/-/solid-testing-library-0.3.0.tgz", + "integrity": "sha512-6NWVbySNVzyReBm2N6p3eF8bzxRZXHZTAmPix4vFWYol16QWVjNQsEUxvr+ZOutb0yuMZmNuGx3b6WIJYmjwMQ==", + "dev": true, + "requires": { + "@testing-library/dom": "^7.29.4" + }, + "dependencies": { + "@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@testing-library/dom": { + "version": "7.31.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.31.2.tgz", + "integrity": "sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.6", + "lz-string": "^1.4.4", + "pretty-format": "^26.6.2" + } + }, + "@types/yargs": { + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.14.tgz", + "integrity": "sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "requires": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + } + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } }, "sort-by": { "version": "1.2.0", @@ -70245,6 +68974,12 @@ } } }, + "ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "dev": true + }, "tsconfig-paths": { "version": "3.14.1", "dev": true, @@ -70325,7 +69060,9 @@ } }, "typescript": { - "version": "4.7.4" + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==" }, "ua-parser-js": { "version": "0.7.31" @@ -70427,8 +69164,6 @@ }, "universal-user-agent": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", "dev": true }, "universalify": { @@ -70480,9 +69215,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.6.tgz", - "integrity": "sha512-We7BqM9XFlcW94Op93uW8+2LXvGezs7QA0WY+f1H7RR1q46B06W6hZF6LbmOlpCS1HU22q/6NOGTGW5sCm7NJQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz", + "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==", "requires": { "escalade": "^3.1.1", "picocolors": "^1.0.0" @@ -70655,6 +69390,69 @@ "extsprintf": "^1.2.0" } }, + "vite": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-3.1.3.tgz", + "integrity": "sha512-/3XWiktaopByM5bd8dqvHxRt5EEgRikevnnrpND0gRfNkrMrPaGGexhtLCzv15RcCMtV2CLw+BPas8YFeSG0KA==", + "dev": true, + "requires": { + "esbuild": "^0.15.6", + "fsevents": "~2.3.2", + "postcss": "^8.4.16", + "resolve": "^1.22.1", + "rollup": "~2.78.0" + }, + "dependencies": { + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + } + } + }, + "vite-plugin-solid": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.3.6.tgz", + "integrity": "sha512-jzpurBMtwAqpNIp9lJU1PR0JDDAsuagojADVqcwsaCUIGYIwQ83+rYB/uZa+UGP4KvV397xo0NWCKHMkgW5x4Q==", + "dev": true, + "requires": { + "@babel/core": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "babel-preset-solid": "^1.4.6", + "merge-anything": "^5.0.2", + "solid-refresh": "^0.4.1" + }, + "dependencies": { + "is-what": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.7.tgz", + "integrity": "sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==", + "dev": true + }, + "merge-anything": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.0.4.tgz", + "integrity": "sha512-YFsDeY5A9SLXhL21Qn15wCWewRUW6wMTxQF4SuPe9bNdr1wsjiE44Rp8FQUTCtwO0WLdlKiFzhAVE5tlf857Tg==", + "dev": true, + "requires": { + "is-what": "^4.1.7", + "ts-toolbelt": "^9.6.0" + } + } + } + }, "vlq": { "version": "1.0.1" }, diff --git a/package.json b/package.json index 4c0404b286..6e540af4e2 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "./packages/react-query", "./packages/react-query-devtools", "./packages/react-query-persist-client", + "./packages/solid-query", "./examples/react/auto-refetching", "./examples/react/basic", "./examples/react/basic-graphql-request", @@ -52,7 +53,11 @@ "./examples/react/rick-morty", "./examples/react/simple", "./examples/react/star-wars", - "./examples/react/suspense" + "./examples/react/suspense", + "./examples/solid/basic-typescript", + "./examples/solid/simple", + "./examples/solid/basic-graphql-request", + "./examples/solid/default-query-function" ], "devDependencies": { "@babel/core": "^7.17.9", @@ -67,8 +72,8 @@ "@rollup/plugin-replace": "^4.0.0", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.0.0", - "@testing-library/react-hooks": "^7.0.2", "@testing-library/react-17": "npm:@testing-library/react@12.1.4", + "@testing-library/react-hooks": "^7.0.2", "@tsconfig/svelte": "^3.0.0", "@types/jest": "^26.0.4", "@types/luxon": "^2.3.1", @@ -80,6 +85,7 @@ "axios": "^0.26.1", "babel-eslint": "^10.1.0", "babel-plugin-transform-async-to-promises": "^0.8.18", + "babel-preset-solid": "^1.5.4", "bundlewatch": "^0.3.2", "concurrently": "^7.1.0", "current-git-branch": "^1.1.0", @@ -112,7 +118,9 @@ "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-visualizer": "^5.6.0", - "solid-js": "^1.3.15", + "solid-jest": "^0.2.0", + "solid-js": "^1.5.4", + "solid-testing-library": "^0.3.0", "stream-to-array": "^2.3.0", "svelte": "^3.48.0", "ts-node": "^10.7.0", diff --git a/packages/solid-query/.eslintrc b/packages/solid-query/.eslintrc new file mode 100644 index 0000000000..a1437a22aa --- /dev/null +++ b/packages/solid-query/.eslintrc @@ -0,0 +1,10 @@ +{ + "parserOptions": { + "project": "./tsconfig.lint.json", + "sourceType": "module" + }, + "rules": { + "react/react-in-jsx-scope": "off", + "react-hooks/rules-of-hooks": "off" + } +} diff --git a/packages/solid-query/package.json b/packages/solid-query/package.json new file mode 100644 index 0000000000..38d137af1e --- /dev/null +++ b/packages/solid-query/package.json @@ -0,0 +1,52 @@ +{ + "name": "@tanstack/solid-query", + "version": "4.3.9", + "description": "Primitives for managing, caching and syncing asynchronous and remote data in Solid", + "author": "tannerlinsley", + "license": "MIT", + "repository": "tanstack/query", + "homepage": "https://tanstack.com/query", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "types": "build/lib/index.d.ts", + "main": "build/lib/index.js", + "module": "build/lib/index.esm.js", + "exports": { + ".": { + "types": "./build/lib/index.d.ts", + "solid": "./build/solid/index.js", + "import": "./build/lib/index.mjs", + "browser": "./build/lib/index.mjs", + "require": "./build/lib/index.js", + "node": "./build/lib/index.js", + "default": "./build/lib/index.js" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "scripts": { + "clean": "rm -rf ./build", + "test:eslint": "../../node_modules/.bin/eslint --ext .ts,.tsx ./src", + "test:jest": "../../node_modules/.bin/jest --config jest.config.js", + "test:jest:dev": "yarn test:jest --watch" + }, + "files": [ + "build/lib/*", + "build/umd/*", + "build/solid/*", + "src" + ], + "devDependencies": { + "@types/jscodeshift": "^0.11.3", + "jscodeshift": "^0.13.1" + }, + "dependencies": { + "@tanstack/query-core": "4.3.8" + }, + "peerDependencies": { + "solid-js": "^1.5.4" + }, + "peerDependenciesMeta": {} +} diff --git a/packages/solid-query/src/QueryClientProvider.tsx b/packages/solid-query/src/QueryClientProvider.tsx new file mode 100644 index 0000000000..c52c6a8409 --- /dev/null +++ b/packages/solid-query/src/QueryClientProvider.tsx @@ -0,0 +1,100 @@ +import type { QueryClient } from '@tanstack/query-core' +import type { Context, JSX } from 'solid-js' +import { + createContext, + useContext, + onMount, + onCleanup, + mergeProps, +} from 'solid-js' +import type { ContextOptions } from './types' + +declare global { + interface Window { + SolidQueryClientContext?: Context + } +} + +export const defaultContext = createContext(undefined) +const QueryClientSharingContext = createContext(false) + +// If we are given a context, we will use it. +// Otherwise, if contextSharing is on, we share the first and at least one +// instance of the context across the window +// to ensure that if Solid Query is used across +// different bundles or microfrontends they will +// all use the same **instance** of context, regardless +// of module scoping. +function getQueryClientContext( + context: Context | undefined, + contextSharing: boolean, +) { + if (context) { + return context + } + if (contextSharing && typeof window !== 'undefined') { + if (!window.SolidQueryClientContext) { + window.SolidQueryClientContext = defaultContext + } + + return window.SolidQueryClientContext + } + + return defaultContext +} + +export const useQueryClient = ({ context }: ContextOptions = {}) => { + const queryClient = useContext( + getQueryClientContext(context, useContext(QueryClientSharingContext)), + ) + + if (!queryClient) { + throw new Error('No QueryClient set, use QueryClientProvider to set one') + } + + return queryClient +} + +type QueryClientProviderPropsBase = { + client: QueryClient + children?: JSX.Element +} +type QueryClientProviderPropsWithContext = ContextOptions & { + contextSharing?: never +} & QueryClientProviderPropsBase +type QueryClientProviderPropsWithContextSharing = { + context?: never + contextSharing?: boolean +} & QueryClientProviderPropsBase + +export type QueryClientProviderProps = + | QueryClientProviderPropsWithContext + | QueryClientProviderPropsWithContextSharing + +export const QueryClientProvider = ( + props: QueryClientProviderProps, +): JSX.Element => { + const mergedProps = mergeProps( + { + contextSharing: false, + }, + props, + ) + onMount(() => mergedProps.client.mount()) + onCleanup(() => mergedProps.client.unmount()) + + const QueryClientContext = getQueryClientContext( + mergedProps.context, + mergedProps.contextSharing, + ) + + return ( + + + {mergedProps.children} + + + ) +} diff --git a/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx new file mode 100644 index 0000000000..1ac05227c5 --- /dev/null +++ b/packages/solid-query/src/__tests__/QueryClientProvider.test.tsx @@ -0,0 +1,267 @@ +import { render, screen, waitFor } from 'solid-testing-library' +import { queryKey } from './utils' + +import { QueryCache, QueryClient } from '@tanstack/query-core' +import type { Context } from 'solid-js' +import { createContext, useContext } from 'solid-js' +import { renderToString } from 'solid-js/web' +import { createQuery, QueryClientProvider, useQueryClient } from '..' +import { createQueryClient, sleep } from './utils' + +describe('QueryClientProvider', () => { + it('sets a specific cache for all queries to use', async () => { + const key = queryKey() + + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + function Page() { + const query = createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + return ( +
+

{query.data}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => { + return screen.getByText('test') + }) + + expect(queryCache.find(key())).toBeDefined() + }) + + it('allows multiple caches to be partitioned', async () => { + const key1 = queryKey() + const key2 = queryKey() + + const queryCache1 = new QueryCache() + const queryCache2 = new QueryCache() + + const queryClient1 = createQueryClient({ queryCache: queryCache1 }) + const queryClient2 = createQueryClient({ queryCache: queryCache2 }) + + function Page1() { + const query = createQuery(key1, async () => { + await sleep(10) + return 'test1' + }) + + return ( +
+

{query.data}

+
+ ) + } + function Page2() { + const query = createQuery(key2, async () => { + await sleep(10) + return 'test2' + }) + + return ( +
+

{query.data}

+
+ ) + } + + render(() => ( + <> + + + + + + + + )) + + await waitFor(() => screen.getByText('test1')) + await waitFor(() => screen.getByText('test2')) + + expect(queryCache1.find(key1())).toBeDefined() + expect(queryCache1.find(key2())).not.toBeDefined() + expect(queryCache2.find(key1())).not.toBeDefined() + expect(queryCache2.find(key2())).toBeDefined() + }) + + it("uses defaultOptions for queries when they don't provide their own config", async () => { + const key = queryKey() + + const queryCache = new QueryCache() + const queryClient = createQueryClient({ + queryCache, + defaultOptions: { + queries: { + cacheTime: Infinity, + }, + }, + }) + + function Page() { + const query = createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + return ( +
+

{query.data}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('test')) + + expect(queryCache.find(key())).toBeDefined() + expect(queryCache.find(key())?.options.cacheTime).toBe(Infinity) + }) + + describe('with custom context', () => { + it('uses the correct context', async () => { + const key = queryKey() + + const contextOuter = createContext(undefined) + const contextInner = createContext(undefined) + + const queryCacheOuter = new QueryCache() + const queryClientOuter = new QueryClient({ queryCache: queryCacheOuter }) + + const queryCacheInner = new QueryCache() + const queryClientInner = new QueryClient({ queryCache: queryCacheInner }) + + const queryCacheInnerInner = new QueryCache() + const queryClientInnerInner = new QueryClient({ + queryCache: queryCacheInnerInner, + }) + + function Page() { + const queryOuter = createQuery(key, async () => 'testOuter', { + context: contextOuter, + }) + const queryInner = createQuery(key, async () => 'testInner', { + context: contextInner, + }) + const queryInnerInner = createQuery(key, async () => 'testInnerInner') + + return ( +
+

+ {queryOuter.data} {queryInner.data} {queryInnerInner.data} +

+
+ ) + } + + // contextSharing should be ignored when passing a custom context. + const contextSharing = true + + render(() => ( + + + + + + + + )) + + await waitFor(() => + screen.getByText('testOuter testInner testInnerInner'), + ) + }) + }) + + describe('useQueryClient', () => { + it('should throw an error if no query client has been set', () => { + const consoleMock = jest + .spyOn(console, 'error') + .mockImplementation(() => undefined) + + function Page() { + useQueryClient() + return null + } + + expect(() => render(() => )).toThrow( + 'No QueryClient set, use QueryClientProvider to set one', + ) + + consoleMock.mockRestore() + }) + + it('should use window to get the context when contextSharing is true', () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + let queryClientFromHook: QueryClient | undefined + let queryClientFromWindow: QueryClient | undefined + + function Page() { + queryClientFromHook = useQueryClient() + queryClientFromWindow = useContext( + window.SolidQueryClientContext as Context, + ) + return null + } + + render(() => ( + + + + )) + + expect(queryClientFromHook).toEqual(queryClient) + expect(queryClientFromWindow).toEqual(queryClient) + }) + + it.skip('should not use window to get the context when contextSharing is true and window does not exist', () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + // Mock a non web browser environment + const windowSpy = jest + .spyOn(window, 'window', 'get') + .mockImplementation(undefined) + + let queryClientFromHook: QueryClient | undefined + + function Page() { + queryClientFromHook = useQueryClient() + return null + } + + // TODO(lukemurray): fails because renderToString never calls Page + // probably an SSR-testing issue we need to fix. + renderToString(() => ( + + + + )) + + expect(queryClientFromHook).toEqual(queryClient) + + windowSpy.mockRestore() + }) + }) +}) diff --git a/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx new file mode 100644 index 0000000000..ceecc73bba --- /dev/null +++ b/packages/solid-query/src/__tests__/createInfiniteQuery.test.tsx @@ -0,0 +1,1889 @@ +import { fireEvent, render, screen, waitFor } from 'solid-testing-library' + +import { createQueryClient, sleep } from './utils' + +import { + createEffect, + createRenderEffect, + createSignal, + For, + Index, + Match, + Switch, +} from 'solid-js' +import type { + CreateInfiniteQueryResult, + InfiniteData, + QueryFunctionContext, +} from '..' +import { createInfiniteQuery, QueryCache, QueryClientProvider } from '..' +import { Blink, queryKey, setActTimeout } from './utils' + +interface Result { + items: number[] + nextId?: number + prevId?: number + ts: number +} + +const pageSize = 10 + +const fetchItems = async ( + page: number, + ts: number, + noNext?: boolean, + noPrev?: boolean, +): Promise => { + await sleep(10) + return { + items: [...new Array(10)].fill(null).map((_, d) => page * pageSize + d), + nextId: noNext ? undefined : page + 1, + prevId: noPrev ? undefined : page - 1, + ts, + } +} + +describe('useInfiniteQuery', () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + it('should return the correct states for a successful query', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + ({ pageParam = 0 }) => Number(pageParam), + { + getNextPageParam: (lastPage) => lastPage + 1, + }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toEqual({ + data: undefined, + dataUpdatedAt: 0, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + fetchNextPage: expect.any(Function), + fetchPreviousPage: expect.any(Function), + hasNextPage: undefined, + hasPreviousPage: undefined, + isError: false, + isFetched: false, + isFetchedAfterMount: false, + isFetching: true, + isPaused: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + isLoading: true, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: false, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'loading', + fetchStatus: 'fetching', + }) + + expect(states[1]).toEqual({ + data: { pages: [0], pageParams: [undefined] }, + dataUpdatedAt: expect.any(Number), + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + fetchNextPage: expect.any(Function), + fetchPreviousPage: expect.any(Function), + hasNextPage: true, + hasPreviousPage: undefined, + isError: false, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isPaused: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + isLoading: false, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: true, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'success', + fetchStatus: 'idle', + }) + }) + + it('should not throw when fetchNextPage returns an error', async () => { + const key = queryKey() + let noThrow: boolean + + function Page() { + const start = 1 + const state = createInfiniteQuery( + key, + async ({ pageParam = start }) => { + if (pageParam === 2) { + throw new Error('error') + } + return Number(pageParam) + }, + { + retry: 1, + retryDelay: 10, + getNextPageParam: (lastPage) => lastPage + 1, + }, + ) + + createEffect(() => { + const fetchNextPage = state.fetchNextPage + setActTimeout(() => { + fetchNextPage() + .then(() => { + noThrow = true + }) + .catch(() => undefined) + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await waitFor(() => expect(noThrow).toBe(true)) + }) + + it('should keep the previous data when keepPreviousData is set', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const [order, setOrder] = createSignal('desc') + + const state = createInfiniteQuery( + () => [key(), order()], + async ({ pageParam = 0 }) => { + await sleep(10) + return `${pageParam}-${order()}` + }, + { + getNextPageParam: () => 1, + keepPreviousData: true, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + +
data: {state.data?.pages.join(',') ?? 'null'}
+
isFetching: {String(state.isFetching)}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 0-desc')) + fireEvent.click(screen.getByRole('button', { name: /fetchNextPage/i })) + + await waitFor(() => screen.getByText('data: 0-desc,1-desc')) + fireEvent.click(screen.getByRole('button', { name: /order/i })) + + await waitFor(() => screen.getByText('data: 0-asc')) + await waitFor(() => screen.getByText('isFetching: false')) + await waitFor(() => expect(states.length).toBe(6)) + + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + isPreviousData: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: ['0-desc'] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + isPreviousData: false, + }) + expect(states[2]).toMatchObject({ + data: { pages: ['0-desc'] }, + isFetching: true, + isFetchingNextPage: true, + isSuccess: true, + isPreviousData: false, + }) + expect(states[3]).toMatchObject({ + data: { pages: ['0-desc', '1-desc'] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + isPreviousData: false, + }) + // Set state + expect(states[4]).toMatchObject({ + data: { pages: ['0-desc', '1-desc'] }, + isFetching: true, + isFetchingNextPage: false, + isSuccess: true, + isPreviousData: true, + }) + expect(states[5]).toMatchObject({ + data: { pages: ['0-asc'] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + isPreviousData: false, + }) + }) + + it('should be able to select a part of the data', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery(key, () => ({ count: 1 }), { + select: (data) => ({ + pages: data.pages.map((x) => `count: ${x.count}`), + pageParams: data.pageParams, + }), + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: undefined, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: ['count: 1'] }, + isSuccess: true, + }) + }) + + it('should be able to select a new result and not cause infinite renders', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult<{ count: number; id: number }>[] = + [] + let selectCalled = 0 + + function Page() { + const state = createInfiniteQuery(key, () => ({ count: 1 }), { + select: (data: InfiniteData<{ count: number }>) => { + selectCalled++ + return { + pages: data.pages.map((x) => ({ ...x, id: Math.random() })), + pageParams: data.pageParams, + } + }, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(20) + + expect(states.length).toBe(2) + expect(selectCalled).toBe(1) + expect(states[0]).toMatchObject({ + data: undefined, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: [{ count: 1 }] }, + isSuccess: true, + }) + }) + + it('should be able to reverse the data', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + async ({ pageParam = 0 }) => { + await sleep(10) + return Number(pageParam) + }, + { + select: (data) => ({ + pages: [...data.pages].reverse(), + pageParams: [...data.pageParams].reverse(), + }), + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ +
data: {state.data?.pages.join(',') ?? 'null'}
+
isFetching: {state.isFetching}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 0')) + fireEvent.click(screen.getByRole('button', { name: /fetchNextPage/i })) + + await waitFor(() => screen.getByText('data: 1,0')) + + await waitFor(() => expect(states.length).toBe(4)) + expect(states[0]).toMatchObject({ + data: undefined, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: [0] }, + isSuccess: true, + }) + expect(states[2]).toMatchObject({ + data: { pages: [0] }, + isSuccess: true, + }) + expect(states[3]).toMatchObject({ + data: { pages: [1, 0] }, + isSuccess: true, + }) + }) + + it('should be able to fetch a previous page', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const start = 10 + const state = createInfiniteQuery( + key, + async ({ pageParam = start }) => { + await sleep(10) + return Number(pageParam) + }, + { + getPreviousPageParam: (firstPage) => firstPage - 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const fetchPreviousPage = state.fetchPreviousPage + setActTimeout(() => { + fetchPreviousPage() + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(4) + expect(states[0]).toMatchObject({ + data: undefined, + hasNextPage: undefined, + hasPreviousPage: undefined, + isFetching: true, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: [10] }, + hasNextPage: undefined, + hasPreviousPage: true, + isFetching: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + isSuccess: true, + }) + expect(states[2]).toMatchObject({ + data: { pages: [10] }, + hasNextPage: undefined, + hasPreviousPage: true, + isFetching: true, + isFetchingNextPage: false, + isFetchingPreviousPage: true, + isSuccess: true, + }) + expect(states[3]).toMatchObject({ + data: { pages: [9, 10] }, + hasNextPage: undefined, + hasPreviousPage: true, + isFetching: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + isSuccess: true, + }) + }) + + it('should be able to refetch when providing page params manually', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery(key, async ({ pageParam = 10 }) => { + await sleep(10) + return Number(pageParam) + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + + +
data: {state.data?.pages.join(',') ?? 'null'}
+
isFetching: {String(state.isFetching)}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 10')) + fireEvent.click(screen.getByRole('button', { name: /fetchNextPage/i })) + + await waitFor(() => screen.getByText('data: 10,11')) + fireEvent.click(screen.getByRole('button', { name: /fetchPreviousPage/i })) + await waitFor(() => screen.getByText('data: 9,10,11')) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + + await waitFor(() => screen.getByText('isFetching: false')) + await waitFor(() => expect(states.length).toBe(8)) + + // Initial fetch + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isFetchingNextPage: false, + }) + // Initial fetch done + expect(states[1]).toMatchObject({ + data: { pages: [10] }, + isFetching: false, + isFetchingNextPage: false, + }) + // Fetch next page + expect(states[2]).toMatchObject({ + data: { pages: [10] }, + isFetching: true, + isFetchingNextPage: true, + }) + // Fetch next page done + expect(states[3]).toMatchObject({ + data: { pages: [10, 11] }, + isFetching: false, + isFetchingNextPage: false, + }) + // Fetch previous page + expect(states[4]).toMatchObject({ + data: { pages: [10, 11] }, + isFetching: true, + isFetchingNextPage: false, + isFetchingPreviousPage: true, + }) + // Fetch previous page done + expect(states[5]).toMatchObject({ + data: { pages: [9, 10, 11] }, + isFetching: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + }) + // Refetch + expect(states[6]).toMatchObject({ + data: { pages: [9, 10, 11] }, + isFetching: true, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + }) + // Refetch done + expect(states[7]).toMatchObject({ + data: { pages: [9, 10, 11] }, + isFetching: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + }) + }) + + it('should be able to refetch when providing page params automatically', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + async ({ pageParam = 10 }) => { + await sleep(10) + return Number(pageParam) + }, + { + getPreviousPageParam: (firstPage) => firstPage - 1, + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + + +
data: {state.data?.pages.join(',') ?? 'null'}
+
isFetching: {String(state.isFetching)}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 10')) + fireEvent.click(screen.getByRole('button', { name: /fetchNextPage/i })) + + await waitFor(() => screen.getByText('data: 10,11')) + fireEvent.click(screen.getByRole('button', { name: /fetchPreviousPage/i })) + await waitFor(() => screen.getByText('data: 9,10,11')) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + + await waitFor(() => screen.getByText('isFetching: false')) + await waitFor(() => expect(states.length).toBe(8)) + + // Initial fetch + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isFetchingNextPage: false, + }) + // Initial fetch done + expect(states[1]).toMatchObject({ + data: { pages: [10] }, + isFetching: false, + isFetchingNextPage: false, + }) + // Fetch next page + expect(states[2]).toMatchObject({ + data: { pages: [10] }, + isFetching: true, + isFetchingNextPage: true, + }) + // Fetch next page done + expect(states[3]).toMatchObject({ + data: { pages: [10, 11] }, + isFetching: false, + isFetchingNextPage: false, + }) + // Fetch previous page + expect(states[4]).toMatchObject({ + data: { pages: [10, 11] }, + isFetching: true, + isFetchingNextPage: false, + isFetchingPreviousPage: true, + }) + // Fetch previous page done + expect(states[5]).toMatchObject({ + data: { pages: [9, 10, 11] }, + isFetching: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + }) + // Refetch + expect(states[6]).toMatchObject({ + data: { pages: [9, 10, 11] }, + isFetching: true, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + }) + // Refetch done + expect(states[7]).toMatchObject({ + data: { pages: [9, 10, 11] }, + isFetching: false, + isFetchingNextPage: false, + isFetchingPreviousPage: false, + }) + }) + + it('should be able to refetch only specific pages when refetchPages is provided', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + let multiplier = 1 + const state = createInfiniteQuery( + key, + async ({ pageParam = 10 }) => { + await sleep(10) + return Number(pageParam) * multiplier + }, + { + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + +
data: {state.data?.pages.join(',') ?? 'null'}
+
isFetching: {String(state.isFetching)}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 10')) + fireEvent.click(screen.getByRole('button', { name: /fetchNextPage/i })) + + await waitFor(() => screen.getByText('data: 10,11')) + fireEvent.click(screen.getByRole('button', { name: /refetchPage/i })) + + await waitFor(() => screen.getByText('data: 20,11')) + await waitFor(() => screen.getByText('isFetching: false')) + await waitFor(() => expect(states.length).toBe(6)) + + // Initial fetch + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isFetchingNextPage: false, + }) + // Initial fetch done + expect(states[1]).toMatchObject({ + data: { pages: [10] }, + isFetching: false, + isFetchingNextPage: false, + }) + // Fetch next page + expect(states[2]).toMatchObject({ + data: { pages: [10] }, + isFetching: true, + isFetchingNextPage: true, + }) + // Fetch next page done + expect(states[3]).toMatchObject({ + data: { pages: [10, 11] }, + isFetching: false, + isFetchingNextPage: false, + }) + // Refetch + expect(states[4]).toMatchObject({ + data: { pages: [10, 11] }, + isFetching: true, + isFetchingNextPage: false, + }) + // Refetch done, only page one has been refetched and multiplied + expect(states[5]).toMatchObject({ + data: { pages: [20, 11] }, + isFetching: false, + isFetchingNextPage: false, + }) + }) + + it('should silently cancel any ongoing fetch when fetching more', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const start = 10 + const state = createInfiniteQuery( + key, + async ({ pageParam = start }) => { + await sleep(50) + return Number(pageParam) + }, + { + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const { refetch, fetchNextPage } = state + setActTimeout(() => { + refetch() + }, 100) + setActTimeout(() => { + fetchNextPage() + }, 110) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(300) + + expect(states.length).toBe(5) + expect(states[0]).toMatchObject({ + hasNextPage: undefined, + data: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + hasNextPage: true, + data: { pages: [10] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[2]).toMatchObject({ + hasNextPage: true, + data: { pages: [10] }, + isFetching: true, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[3]).toMatchObject({ + hasNextPage: true, + data: { pages: [10] }, + isFetching: true, + isFetchingNextPage: true, + isSuccess: true, + }) + expect(states[4]).toMatchObject({ + hasNextPage: true, + data: { pages: [10, 11] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should silently cancel an ongoing fetchNextPage request when another fetchNextPage is invoked', async () => { + const key = queryKey() + const start = 10 + const onAborts: jest.Mock[] = [] + const abortListeners: jest.Mock[] = [] + const fetchPage = jest.fn< + Promise, + [QueryFunctionContext, number>] + >(async ({ pageParam = start, signal }) => { + if (signal) { + const onAbort = jest.fn() + const abortListener = jest.fn() + onAborts.push(onAbort) + abortListeners.push(abortListener) + signal.onabort = onAbort + signal.addEventListener('abort', abortListener) + } + await sleep(50) + return Number(pageParam) + }) + + function Page() { + const state = createInfiniteQuery(key, fetchPage, { + getNextPageParam: (lastPage) => lastPage + 1, + }) + + createEffect(() => { + const { fetchNextPage } = state + setActTimeout(() => { + fetchNextPage() + }, 100) + setActTimeout(() => { + fetchNextPage() + }, 110) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(300) + + const expectedCallCount = 3 + expect(fetchPage).toBeCalledTimes(expectedCallCount) + expect(onAborts).toHaveLength(expectedCallCount) + expect(abortListeners).toHaveLength(expectedCallCount) + + let callIndex = 0 + const firstCtx = fetchPage.mock.calls[callIndex]![0] + expect(firstCtx.pageParam).toBeUndefined() + expect(firstCtx.queryKey).toEqual(key()) + if (typeof AbortSignal === 'function') { + expect(firstCtx.signal).toBeInstanceOf(AbortSignal) + expect(firstCtx.signal?.aborted).toBe(false) + expect(onAborts[callIndex]).not.toHaveBeenCalled() + expect(abortListeners[callIndex]).not.toHaveBeenCalled() + } + + callIndex = 1 + const secondCtx = fetchPage.mock.calls[callIndex]![0] + expect(secondCtx.pageParam).toBe(11) + expect(secondCtx.queryKey).toEqual(key()) + if (typeof AbortSignal === 'function') { + expect(secondCtx.signal).toBeInstanceOf(AbortSignal) + expect(secondCtx.signal?.aborted).toBe(true) + expect(onAborts[callIndex]).toHaveBeenCalledTimes(1) + expect(abortListeners[callIndex]).toHaveBeenCalledTimes(1) + } + + callIndex = 2 + const thirdCtx = fetchPage.mock.calls[callIndex]![0] + expect(thirdCtx.pageParam).toBe(11) + expect(thirdCtx.queryKey).toEqual(key()) + if (typeof AbortSignal === 'function') { + expect(thirdCtx.signal).toBeInstanceOf(AbortSignal) + expect(thirdCtx.signal?.aborted).toBe(false) + expect(onAborts[callIndex]).not.toHaveBeenCalled() + expect(abortListeners[callIndex]).not.toHaveBeenCalled() + } + }) + + it('should not cancel an ongoing fetchNextPage request when another fetchNextPage is invoked if `cancelRefetch: false` is used ', async () => { + const key = queryKey() + const start = 10 + const onAborts: jest.Mock[] = [] + const abortListeners: jest.Mock[] = [] + const fetchPage = jest.fn< + Promise, + [QueryFunctionContext, number>] + >(async ({ pageParam = start, signal }) => { + if (signal) { + const onAbort = jest.fn() + const abortListener = jest.fn() + onAborts.push(onAbort) + abortListeners.push(abortListener) + signal.onabort = onAbort + signal.addEventListener('abort', abortListener) + } + await sleep(50) + return Number(pageParam) + }) + + function Page() { + const state = createInfiniteQuery(key, fetchPage, { + getNextPageParam: (lastPage) => lastPage + 1, + }) + + createEffect(() => { + const { fetchNextPage } = state + setActTimeout(() => { + fetchNextPage() + }, 100) + setActTimeout(() => { + fetchNextPage({ cancelRefetch: false }) + }, 110) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(300) + + const expectedCallCount = 2 + expect(fetchPage).toBeCalledTimes(expectedCallCount) + expect(onAborts).toHaveLength(expectedCallCount) + expect(abortListeners).toHaveLength(expectedCallCount) + + let callIndex = 0 + const firstCtx = fetchPage.mock.calls[callIndex]![0] + expect(firstCtx.pageParam).toBeUndefined() + expect(firstCtx.queryKey).toEqual(key()) + if (typeof AbortSignal === 'function') { + expect(firstCtx.signal).toBeInstanceOf(AbortSignal) + expect(firstCtx.signal?.aborted).toBe(false) + expect(onAborts[callIndex]).not.toHaveBeenCalled() + expect(abortListeners[callIndex]).not.toHaveBeenCalled() + } + + callIndex = 1 + const secondCtx = fetchPage.mock.calls[callIndex]![0] + expect(secondCtx.pageParam).toBe(11) + expect(secondCtx.queryKey).toEqual(key()) + if (typeof AbortSignal === 'function') { + expect(secondCtx.signal).toBeInstanceOf(AbortSignal) + expect(secondCtx.signal?.aborted).toBe(false) + expect(onAborts[callIndex]).not.toHaveBeenCalled() + expect(abortListeners[callIndex]).not.toHaveBeenCalled() + } + }) + + it('should keep fetching first page when not loaded yet and triggering fetch more', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const start = 10 + const state = createInfiniteQuery( + key, + async ({ pageParam = start }) => { + await sleep(50) + return Number(pageParam) + }, + { + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const { fetchNextPage } = state + setActTimeout(() => { + fetchNextPage() + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + hasNextPage: undefined, + data: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + hasNextPage: true, + data: { pages: [10] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should stop fetching additional pages when the component is unmounted and AbortSignal is consumed', async () => { + const key = queryKey() + let fetches = 0 + + const initialData = { pages: [1, 2, 3, 4], pageParams: [0, 1, 2, 3] } + + function List() { + createInfiniteQuery( + key, + async ({ pageParam = 0, signal: _ }) => { + fetches++ + await sleep(50) + return Number(pageParam) * 10 + }, + { + initialData, + getNextPageParam: (_, allPages) => { + return allPages.length === 4 ? undefined : allPages.length + }, + }, + ) + + return null + } + + function Page() { + const [show, setShow] = createSignal(true) + + createEffect(() => { + setActTimeout(() => { + setShow(false) + }, 75) + }) + + return <>{show() ? : null} + } + + render(() => ( + + + + )) + + await sleep(300) + + if (typeof AbortSignal === 'function') { + expect(fetches).toBe(2) + expect(queryClient.getQueryState(key())).toMatchObject({ + data: initialData, + status: 'success', + error: null, + }) + } else { + // if AbortSignal is not consumed, fetches should not abort + expect(fetches).toBe(4) + expect(queryClient.getQueryState(key())).toMatchObject({ + data: { pages: [0, 10, 20, 30], pageParams: [0, 1, 2, 3] }, + status: 'success', + error: null, + }) + } + }) + + it('should be able to override the cursor in the fetchNextPage callback', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + async ({ pageParam = 0 }) => { + await sleep(10) + return Number(pageParam) + }, + { + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const { fetchNextPage } = state + setActTimeout(() => { + fetchNextPage({ pageParam: 5 }) + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(4) + expect(states[0]).toMatchObject({ + hasNextPage: undefined, + data: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + hasNextPage: true, + data: { pages: [0] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[2]).toMatchObject({ + hasNextPage: true, + data: { pages: [0] }, + isFetching: true, + isFetchingNextPage: true, + isSuccess: true, + }) + expect(states[3]).toMatchObject({ + hasNextPage: true, + data: { pages: [0, 5] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should be able to set new pages with the query client', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const [firstPage, setFirstPage] = createSignal(0) + + const state = createInfiniteQuery( + key, + async ({ pageParam = firstPage() }) => { + await sleep(10) + return Number(pageParam) + }, + { + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const { refetch } = state + setActTimeout(() => { + queryClient.setQueryData(key(), { pages: [7, 8], pageParams: [7, 8] }) + setFirstPage(7) + }, 20) + + setActTimeout(() => { + refetch() + }, 50) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(5) + expect(states[0]).toMatchObject({ + hasNextPage: undefined, + data: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + }) + // After first fetch + expect(states[1]).toMatchObject({ + hasNextPage: true, + data: { pages: [0] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + // Set state + expect(states[2]).toMatchObject({ + hasNextPage: true, + data: { pages: [7, 8] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + // Refetch + expect(states[3]).toMatchObject({ + hasNextPage: true, + data: { pages: [7, 8] }, + isFetching: true, + isFetchingNextPage: false, + isSuccess: true, + }) + // Refetch done + expect(states[4]).toMatchObject({ + hasNextPage: true, + data: { pages: [7, 8] }, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should only refetch the first page when initialData is provided', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + async ({ pageParam }): Promise => { + await sleep(10) + return pageParam + }, + { + initialData: { pages: [1], pageParams: [1] }, + getNextPageParam: (lastPage) => lastPage + 1, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const { fetchNextPage } = state + setActTimeout(() => { + fetchNextPage() + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(4) + expect(states[0]).toMatchObject({ + data: { pages: [1] }, + hasNextPage: true, + isFetching: true, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[1]).toMatchObject({ + data: { pages: [1] }, + hasNextPage: true, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[2]).toMatchObject({ + data: { pages: [1] }, + hasNextPage: true, + isFetching: true, + isFetchingNextPage: true, + isSuccess: true, + }) + expect(states[3]).toMatchObject({ + data: { pages: [1, 2] }, + hasNextPage: true, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should set hasNextPage to false if getNextPageParam returns undefined', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + ({ pageParam = 1 }) => Number(pageParam), + { + getNextPageParam: () => undefined, + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: undefined, + hasNextPage: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: [1] }, + hasNextPage: false, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should compute hasNextPage correctly using initialData', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + ({ pageParam = 10 }): number => pageParam, + { + initialData: { pages: [10], pageParams: [undefined] }, + getNextPageParam: (lastPage) => (lastPage === 10 ? 11 : undefined), + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return null + } + + render(() => ( + + + + )) + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: { pages: [10] }, + hasNextPage: true, + isFetching: true, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[1]).toMatchObject({ + data: { pages: [10] }, + hasNextPage: true, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should compute hasNextPage correctly for falsy getFetchMore return value using initialData', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + ({ pageParam = 10 }): number => pageParam, + { + initialData: { pages: [10], pageParams: [undefined] }, + getNextPageParam: () => undefined, + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return null + } + + render(() => ( + + + + )) + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: { pages: [10] }, + hasNextPage: false, + isFetching: true, + isFetchingNextPage: false, + isSuccess: true, + }) + expect(states[1]).toMatchObject({ + data: { pages: [10] }, + hasNextPage: false, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should not use selected data when computing hasNextPage', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const state = createInfiniteQuery( + key, + ({ pageParam = 1 }) => Number(pageParam), + { + getNextPageParam: (lastPage) => (lastPage === 1 ? 2 : false), + select: (data) => ({ + pages: data.pages.map((x) => x.toString()), + pageParams: data.pageParams, + }), + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: undefined, + hasNextPage: undefined, + isFetching: true, + isFetchingNextPage: false, + isSuccess: false, + }) + expect(states[1]).toMatchObject({ + data: { pages: ['1'] }, + hasNextPage: true, + isFetching: false, + isFetchingNextPage: false, + isSuccess: true, + }) + }) + + it('should build fresh cursors on refetch', async () => { + const key = queryKey() + + const genItems = (size: number) => + [...new Array(size)].fill(null).map((_, d) => d) + const items = genItems(15) + const limit = 3 + + const fetchItemsWithLimit = async (cursor = 0, ts: number) => { + await sleep(10) + return { + nextId: cursor + limit, + items: items.slice(cursor, cursor + limit), + ts, + } + } + + function Page() { + let fetchCountRef = 0 + const state = createInfiniteQuery( + key, + ({ pageParam = 0 }) => fetchItemsWithLimit(pageParam, fetchCountRef++), + { + getNextPageParam: (lastPage) => lastPage.nextId, + }, + ) + + return ( +
+

Pagination

+ +
Data:
+ + {(page, i) => ( +
+
+ Page {i()}: {page.ts} +
+
+ + {(item) =>

Item: {item()}

} +
+
+
+ )} +
+
+ + + +
+
+ {!state.isFetchingNextPage ? 'Background Updating...' : null} +
+ + } + > + Loading... + + Error: {state.error!.message} + +
+
+ ) + } + + render(() => ( + + + + )) + + screen.getByText('Loading...') + + await waitFor(() => screen.getByText('Item: 2')) + await waitFor(() => screen.getByText('Page 0: 0')) + + fireEvent.click(screen.getByText('Load More')) + + await waitFor(() => screen.getByText('Loading more...')) + await waitFor(() => screen.getByText('Item: 5')) + await waitFor(() => screen.getByText('Page 0: 0')) + await waitFor(() => screen.getByText('Page 1: 1')) + + fireEvent.click(screen.getByText('Load More')) + + await waitFor(() => screen.getByText('Loading more...')) + await waitFor(() => screen.getByText('Item: 8')) + await waitFor(() => screen.getByText('Page 0: 0')) + await waitFor(() => screen.getByText('Page 1: 1')) + await waitFor(() => screen.getByText('Page 2: 2')) + + fireEvent.click(screen.getByText('Refetch')) + + await waitFor(() => screen.getByText('Background Updating...')) + await waitFor(() => screen.getByText('Item: 8')) + await waitFor(() => screen.getByText('Page 0: 3')) + await waitFor(() => screen.getByText('Page 1: 4')) + await waitFor(() => screen.getByText('Page 2: 5')) + + // ensure that Item: 4 is rendered before removing it + expect(screen.queryAllByText('Item: 4')).toHaveLength(1) + + // remove Item: 4 + fireEvent.click(screen.getByText('Remove item')) + + await waitFor(() => screen.getByText('Background Updating...')) + // ensure that an additional item is rendered (it means that cursors were properly rebuilt) + await waitFor(() => screen.getByText('Item: 9')) + await waitFor(() => screen.getByText('Page 0: 6')) + await waitFor(() => screen.getByText('Page 1: 7')) + await waitFor(() => screen.getByText('Page 2: 8')) + + // ensure that Item: 4 is no longer rendered + expect(screen.queryAllByText('Item: 4')).toHaveLength(0) + }) + + it('should compute hasNextPage correctly for falsy getFetchMore return value on refetching', async () => { + const key = queryKey() + const MAX = 2 + + function Page() { + let fetchCountRef = 0 + const [isRemovedLastPage, setIsRemovedLastPage] = + createSignal(false) + const state = createInfiniteQuery( + key, + ({ pageParam = 0 }) => + fetchItems( + pageParam, + fetchCountRef++, + pageParam === MAX || (pageParam === MAX - 1 && isRemovedLastPage()), + ), + { + getNextPageParam: (lastPage) => lastPage.nextId, + }, + ) + + return ( +
+

Pagination

+ +
Data:
+ + {(page, i) => ( +
+
+ Page {i()}: {page.ts} +
+
+ + {(item) =>

Item: {item()}

} +
+
+
+ )} +
+
+ + + +
+
+ {state.isFetching && !state.isFetchingNextPage + ? 'Background Updating...' + : null} +
+ + } + > + Loading... + + Error: {state.error!.message} + +
+
+ ) + } + + render(() => ( + + + + )) + + screen.getByText('Loading...') + + await waitFor(() => { + screen.getByText('Item: 9') + screen.getByText('Page 0: 0') + }) + + fireEvent.click(screen.getByText('Load More')) + + await waitFor(() => screen.getByText('Loading more...')) + + await waitFor(() => { + screen.getByText('Item: 19') + screen.getByText('Page 0: 0') + screen.getByText('Page 1: 1') + }) + + fireEvent.click(screen.getByText('Load More')) + + await waitFor(() => screen.getByText('Loading more...')) + + await waitFor(() => { + screen.getByText('Item: 29') + screen.getByText('Page 0: 0') + screen.getByText('Page 1: 1') + screen.getByText('Page 2: 2') + }) + + screen.getByText('Nothing more to load') + + fireEvent.click(screen.getByText('Remove Last Page')) + + await sleep(10) + + fireEvent.click(screen.getByText('Refetch')) + + await waitFor(() => screen.getByText('Background Updating...')) + + await waitFor(() => { + screen.getByText('Page 0: 3') + screen.getByText('Page 1: 4') + }) + + expect(screen.queryByText('Item: 29')).toBeNull() + expect(screen.queryByText('Page 2: 5')).toBeNull() + + screen.getByText('Nothing more to load') + }) + + it('should cancel the query function when there are no more subscriptions', async () => { + const key = queryKey() + let cancelFn: jest.Mock = jest.fn() + + const queryFn = ({ signal }: { signal?: AbortSignal }) => { + const promise = new Promise((resolve, reject) => { + cancelFn = jest.fn(() => reject('Cancelled')) + signal?.addEventListener('abort', cancelFn) + sleep(10).then(() => resolve('OK')) + }) + + return promise + } + + function Page() { + const state = createInfiniteQuery(key, queryFn) + return ( +
+

Status: {state.status}

+
+ ) + } + + render(() => ( + + + + + + )) + + await waitFor(() => screen.getByText('off')) + + if (typeof AbortSignal === 'function') { + expect(cancelFn).toHaveBeenCalled() + } + }) +}) diff --git a/packages/solid-query/src/__tests__/createMutation.test.tsx b/packages/solid-query/src/__tests__/createMutation.test.tsx new file mode 100644 index 0000000000..eb66a647c1 --- /dev/null +++ b/packages/solid-query/src/__tests__/createMutation.test.tsx @@ -0,0 +1,1205 @@ +import '@testing-library/jest-dom' +import { + createContext, + createEffect, + createRenderEffect, + createSignal, + ErrorBoundary, +} from 'solid-js' +import { fireEvent, render, screen, waitFor } from 'solid-testing-library' +import type { QueryClient } from '..' +import { + createMutation, + MutationCache, + QueryCache, + QueryClientProvider, +} from '..' +import type { CreateMutationResult } from '../types' +import { + createQueryClient, + mockNavigatorOnLine, + queryKey, + setActTimeout, + sleep, +} from './utils' + +describe('useMutation', () => { + const queryCache = new QueryCache() + const mutationCache = new MutationCache() + const queryClient = createQueryClient({ queryCache, mutationCache }) + + it('should be able to reset `data`', async () => { + function Page() { + const mutation = createMutation(() => Promise.resolve('mutation')) + + return ( +
+

{mutation.data ?? 'empty'}

+ + +
+ ) + } + + render(() => ( + + + + )) + + expect(screen.getByRole('heading').textContent).toBe('empty') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('mutation') + }) + + fireEvent.click(screen.getByRole('button', { name: /reset/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('empty') + }) + }) + + it('should be able to reset `error`', async () => { + function Page() { + const mutation = createMutation(() => { + const err = new Error('Expected mock error. All is well!') + err.stack = '' + return Promise.reject(err) + }) + + return ( +
+ {mutation.error &&

{mutation.error.message}

} + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => { + expect(screen.queryByRole('heading')).toBeNull() + }) + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe( + 'Expected mock error. All is well!', + ) + }) + + fireEvent.click(screen.getByRole('button', { name: /reset/i })) + + await waitFor(() => { + expect(screen.queryByRole('heading')).toBeNull() + }) + }) + + it('should be able to call `onSuccess` and `onSettled` after each successful mutate', async () => { + const [count, setCount] = createSignal(0) + const onSuccessMock = jest.fn() + const onSettledMock = jest.fn() + + function Page() { + const mutation = createMutation( + (vars: { count: number }) => Promise.resolve(vars.count), + { + onSuccess: (data) => { + onSuccessMock(data) + }, + onSettled: (data) => { + onSettledMock(data) + }, + }, + ) + + return ( +
+

{count()}

+ +
+ ) + } + + render(() => ( + + + + )) + + expect(screen.getByRole('heading').textContent).toBe('0') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('3') + }) + + await waitFor(() => { + expect(onSuccessMock).toHaveBeenCalledTimes(3) + }) + + expect(onSuccessMock).toHaveBeenCalledWith(1) + expect(onSuccessMock).toHaveBeenCalledWith(2) + expect(onSuccessMock).toHaveBeenCalledWith(3) + + await waitFor(() => { + expect(onSettledMock).toHaveBeenCalledTimes(3) + }) + + expect(onSettledMock).toHaveBeenCalledWith(1) + expect(onSettledMock).toHaveBeenCalledWith(2) + expect(onSettledMock).toHaveBeenCalledWith(3) + }) + + it('should be able to call `onError` and `onSettled` after each failed mutate', async () => { + const onErrorMock = jest.fn() + const onSettledMock = jest.fn() + const [count, setCount] = createSignal(0) + + function Page() { + const mutation = createMutation( + (vars: { count: number }) => { + const error = new Error( + `Expected mock error. All is well! ${vars.count}`, + ) + error.stack = '' + return Promise.reject(error) + }, + { + onError: (error: Error) => { + onErrorMock(error.message) + }, + onSettled: (_data, error) => { + onSettledMock(error?.message) + }, + }, + ) + + return ( +
+

{count()}

+ +
+ ) + } + + render(() => ( + + + + )) + + expect(screen.getByRole('heading').textContent).toBe('0') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('3') + }) + + await waitFor(() => { + expect(onErrorMock).toHaveBeenCalledTimes(3) + }) + expect(onErrorMock).toHaveBeenCalledWith( + 'Expected mock error. All is well! 1', + ) + expect(onErrorMock).toHaveBeenCalledWith( + 'Expected mock error. All is well! 2', + ) + expect(onErrorMock).toHaveBeenCalledWith( + 'Expected mock error. All is well! 3', + ) + + await waitFor(() => { + expect(onSettledMock).toHaveBeenCalledTimes(3) + }) + expect(onSettledMock).toHaveBeenCalledWith( + 'Expected mock error. All is well! 1', + ) + expect(onSettledMock).toHaveBeenCalledWith( + 'Expected mock error. All is well! 2', + ) + expect(onSettledMock).toHaveBeenCalledWith( + 'Expected mock error. All is well! 3', + ) + }) + + it('should be able to override the useMutation success callbacks', async () => { + const callbacks: string[] = [] + + function Page() { + const mutation = createMutation(async (text: string) => text, { + onSuccess: async () => { + callbacks.push('useMutation.onSuccess') + }, + onSettled: async () => { + callbacks.push('useMutation.onSettled') + }, + }) + + createEffect(() => { + const { mutateAsync } = mutation + setActTimeout(async () => { + try { + const result = await mutateAsync('todo', { + onSuccess: async () => { + callbacks.push('mutateAsync.onSuccess') + }, + onSettled: async () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + callbacks.push(`mutateAsync.result:${result}`) + } catch {} + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(callbacks).toEqual([ + 'useMutation.onSuccess', + 'useMutation.onSettled', + 'mutateAsync.onSuccess', + 'mutateAsync.onSettled', + 'mutateAsync.result:todo', + ]) + }) + + it('should be able to override the error callbacks when using mutateAsync', async () => { + const callbacks: string[] = [] + + function Page() { + const mutation = createMutation( + async (_text: string) => Promise.reject('oops'), + { + onError: async () => { + callbacks.push('useMutation.onError') + }, + onSettled: async () => { + callbacks.push('useMutation.onSettled') + }, + }, + ) + + createEffect(() => { + const { mutateAsync } = mutation + setActTimeout(async () => { + try { + await mutateAsync('todo', { + onError: async () => { + callbacks.push('mutateAsync.onError') + }, + onSettled: async () => { + callbacks.push('mutateAsync.onSettled') + }, + }) + } catch (error) { + callbacks.push(`mutateAsync.error:${error}`) + } + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(callbacks).toEqual([ + 'useMutation.onError', + 'useMutation.onSettled', + 'mutateAsync.onError', + 'mutateAsync.onSettled', + 'mutateAsync.error:oops', + ]) + }) + + it('should be able to use mutation defaults', async () => { + const key = queryKey() + + queryClient.setMutationDefaults(key(), { + mutationFn: async (text: string) => { + await sleep(10) + return text + }, + }) + + const states: CreateMutationResult[] = [] + + function Page() { + const mutation = createMutation(key()) + + createRenderEffect(() => { + states.push({ ...mutation }) + }) + + createEffect(() => { + const { mutate } = mutation + setActTimeout(() => { + mutate('todo') + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(3) + expect(states[0]).toMatchObject({ data: undefined, isLoading: false }) + expect(states[1]).toMatchObject({ data: undefined, isLoading: true }) + expect(states[2]).toMatchObject({ data: 'todo', isLoading: false }) + }) + + it('should be able to retry a failed mutation', async () => { + let count = 0 + + function Page() { + const mutation = createMutation( + (_text: string) => { + count++ + return Promise.reject('oops') + }, + { + retry: 1, + retryDelay: 5, + }, + ) + + createEffect(() => { + const { mutate } = mutation + setActTimeout(() => { + mutate('todo') + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(count).toBe(2) + }) + + it('should not retry mutations while offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + + let count = 0 + + function Page() { + const mutation = createMutation( + (_text: string) => { + count++ + return Promise.reject(new Error('oops')) + }, + { + retry: 1, + retryDelay: 5, + }, + ) + + return ( +
+ +
+ {`error: ${ + mutation.error instanceof Error ? mutation.error.message : 'null' + }, status: ${mutation.status}, isPaused: ${String( + mutation.isPaused, + )}`} +
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => { + expect( + screen.getByText('error: null, status: idle, isPaused: false'), + ).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await waitFor(() => { + expect( + screen.getByText('error: null, status: loading, isPaused: true'), + ).toBeInTheDocument() + }) + + expect(count).toBe(0) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(100) + + await waitFor(() => { + expect( + screen.getByText('error: oops, status: error, isPaused: false'), + ).toBeInTheDocument() + }) + + expect(count).toBe(2) + + onlineMock.mockRestore() + }) + + it('should call onMutate even if paused', async () => { + const onlineMock = mockNavigatorOnLine(false) + const onMutate = jest.fn() + let count = 0 + + function Page() { + const mutation = createMutation( + async (_text: string) => { + count++ + await sleep(10) + return count + }, + { + onMutate, + }, + ) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status}, + isPaused: {String(mutation.isPaused)} +
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('data: null, status: idle, isPaused: false') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await screen.findByText('data: null, status: loading, isPaused: true') + + expect(onMutate).toHaveBeenCalledTimes(1) + expect(onMutate).toHaveBeenCalledWith('todo') + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await screen.findByText('data: 1, status: success, isPaused: false') + + expect(onMutate).toHaveBeenCalledTimes(1) + expect(count).toBe(1) + + onlineMock.mockRestore() + }) + + it('should optimistically go to paused state if offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + let count = 0 + const states: Array = [] + + function Page() { + const mutation = createMutation(async (_text: string) => { + count++ + await sleep(10) + return count + }) + + createRenderEffect(() => { + states.push(`${mutation.status}, ${mutation.isPaused}`) + }) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status}, + isPaused: {String(mutation.isPaused)} +
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('data: null, status: idle, isPaused: false') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await screen.findByText('data: null, status: loading, isPaused: true') + + // no intermediate 'loading, false' state is expected because we don't start mutating! + expect(states[0]).toBe('idle, false') + expect(states[1]).toBe('loading, true') + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await screen.findByText('data: 1, status: success, isPaused: false') + + onlineMock.mockRestore() + }) + + it('should be able to retry a mutation when online', async () => { + const onlineMock = mockNavigatorOnLine(false) + + let count = 0 + const states: CreateMutationResult[] = [] + + function Page() { + const mutation = createMutation( + async (_text: string) => { + await sleep(1) + count++ + return count > 1 ? Promise.resolve('data') : Promise.reject('oops') + }, + { + retry: 1, + retryDelay: 5, + networkMode: 'offlineFirst', + }, + ) + + createRenderEffect(() => { + states.push({ ...mutation }) + }) + + createEffect(() => { + const { mutate } = mutation + setActTimeout(() => { + mutate('todo') + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(50) + + expect(states.length).toBe(4) + expect(states[0]).toMatchObject({ + isLoading: false, + isPaused: false, + failureCount: 0, + }) + expect(states[1]).toMatchObject({ + isLoading: true, + isPaused: false, + failureCount: 0, + }) + expect(states[2]).toMatchObject({ + isLoading: true, + isPaused: false, + failureCount: 1, + }) + expect(states[3]).toMatchObject({ + isLoading: true, + isPaused: true, + failureCount: 1, + }) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(50) + + expect(states.length).toBe(6) + expect(states[4]).toMatchObject({ + isLoading: true, + isPaused: false, + failureCount: 1, + }) + expect(states[5]).toMatchObject({ + isLoading: false, + isPaused: false, + failureCount: 1, + data: 'data', + }) + + onlineMock.mockRestore() + }) + + it('should not change state if unmounted', async () => { + function Mutates() { + const mutation = createMutation(() => sleep(10)) + return + } + function Page() { + const [mounted, setMounted] = createSignal(true) + return ( +
+ + {mounted() && } +
+ ) + } + + render(() => ( + + + + )) + fireEvent.click(screen.getByText('mutate')) + fireEvent.click(screen.getByText('unmount')) + }) + + it('should be able to throw an error when useErrorBoundary is set to true', async () => { + function Page() { + const mutation = createMutation( + () => { + const err = new Error('Expected mock error. All is well!') + err.stack = '' + return Promise.reject(err) + }, + { useErrorBoundary: true }, + ) + + return ( +
+ +
+ ) + } + + render(() => ( + + ( +
+ error +
+ )} + > + +
+
+ )) + + fireEvent.click(screen.getByText('mutate')) + + await waitFor(() => { + expect(screen.queryByText('error')).not.toBeNull() + }) + }) + + it('should be able to throw an error when useErrorBoundary is a function that returns true', async () => { + let boundary = false + function Page() { + const mutation = createMutation( + () => { + const err = new Error('mock error') + err.stack = '' + return Promise.reject(err) + }, + { + useErrorBoundary: () => { + boundary = !boundary + return !boundary + }, + }, + ) + + return ( +
+ + {mutation.error && mutation.error.message} +
+ ) + } + + render(() => ( + + ( +
+ error boundary +
+ )} + > + +
+
+ )) + + // first error goes to component + fireEvent.click(screen.getByText('mutate')) + await waitFor(() => { + expect(screen.queryByText('mock error')).not.toBeNull() + }) + + // second error goes to boundary + fireEvent.click(screen.getByText('mutate')) + await waitFor(() => { + expect(screen.queryByText('error boundary')).not.toBeNull() + }) + }) + + it('should pass meta to mutation', async () => { + const errorMock = jest.fn() + const successMock = jest.fn() + + const queryClientMutationMeta = createQueryClient({ + mutationCache: new MutationCache({ + onSuccess: (_, __, ___, mutation) => { + successMock(mutation.meta?.metaSuccessMessage) + }, + onError: (_, __, ___, mutation) => { + errorMock(mutation.meta?.metaErrorMessage) + }, + }), + }) + + const metaSuccessMessage = 'mutation succeeded' + const metaErrorMessage = 'mutation failed' + + function Page() { + const mutationSucceed = createMutation(async () => '', { + meta: { metaSuccessMessage }, + }) + const mutationError = createMutation( + async () => { + throw new Error('') + }, + { + meta: { metaErrorMessage }, + }, + ) + + return ( +
+ + + {mutationSucceed.isSuccess &&
successTest
} + {mutationError.isError &&
errorTest
} +
+ ) + } + + render(() => ( + + + + )) + + fireEvent.click(screen.getByText('succeed')) + fireEvent.click(screen.getByText('error')) + + await waitFor(() => { + expect(screen.queryByText('successTest')).not.toBeNull() + expect(screen.queryByText('errorTest')).not.toBeNull() + }) + + expect(successMock).toHaveBeenCalledTimes(1) + expect(successMock).toHaveBeenCalledWith(metaSuccessMessage) + expect(errorMock).toHaveBeenCalledTimes(1) + expect(errorMock).toHaveBeenCalledWith(metaErrorMessage) + }) + + it('should call cache callbacks when unmounted', async () => { + const onSuccess = jest.fn() + const onSuccessMutate = jest.fn() + const onSettled = jest.fn() + const onSettledMutate = jest.fn() + const mutationKey = queryKey() + let count = 0 + + function Page() { + const [show, setShow] = createSignal(true) + return ( +
+ + {show() && } +
+ ) + } + + function Component() { + const mutation = createMutation( + async (_text: string) => { + count++ + await sleep(10) + return count + }, + { + mutationKey: mutationKey(), + cacheTime: 0, + onSuccess, + onSettled, + }, + ) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status}, + isPaused: {String(mutation.isPaused)} +
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('data: null, status: idle, isPaused: false') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + fireEvent.click(screen.getByRole('button', { name: /hide/i })) + + await waitFor(() => { + expect( + queryClient.getMutationCache().findAll({ mutationKey: mutationKey() }), + ).toHaveLength(0) + }) + + expect(count).toBe(1) + + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(onSettled).toHaveBeenCalledTimes(1) + expect(onSuccessMutate).toHaveBeenCalledTimes(0) + expect(onSettledMutate).toHaveBeenCalledTimes(0) + }) + + describe('with custom context', () => { + it('should be able to reset `data`', async () => { + const context = createContext(undefined) + + function Page() { + const mutation = createMutation(() => Promise.resolve('mutation'), { + context, + }) + + return ( +
+

{mutation.data ?? 'empty'}

+ + +
+ ) + } + + render(() => ( + + + + )) + + expect(screen.getByRole('heading').textContent).toBe('empty') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('mutation') + }) + + fireEvent.click(screen.getByRole('button', { name: /reset/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('empty') + }) + }) + + it('should throw if the context is not passed to useMutation', async () => { + const context = createContext(undefined) + + function Page() { + const { data = '' } = createMutation(() => Promise.resolve('mutation')) + + return ( +
+

{data}

+
+ ) + } + + render(() => ( + +
error boundary
}> + +
+ , +
+ )) + + await waitFor(() => screen.getByText('error boundary')) + }) + }) + + it('should call mutate callbacks only for the last observer', async () => { + const onSuccess = jest.fn() + const onSuccessMutate = jest.fn() + const onSettled = jest.fn() + const onSettledMutate = jest.fn() + let count = 0 + + function Page() { + const mutation = createMutation( + async (_text: string) => { + count++ + await sleep(10) + return `result${count}` + }, + { + onSuccess, + onSettled, + }, + ) + + return ( +
+ +
+ data: {mutation.data ?? 'null'}, status: {mutation.status} +
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('data: null, status: idle') + + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + fireEvent.click(screen.getByRole('button', { name: /mutate/i })) + + await screen.findByText('data: result2, status: success') + + expect(count).toBe(2) + + expect(onSuccess).toHaveBeenCalledTimes(2) + expect(onSettled).toHaveBeenCalledTimes(2) + expect(onSuccessMutate).toHaveBeenCalledTimes(1) + expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined) + expect(onSettledMutate).toHaveBeenCalledTimes(1) + expect(onSettledMutate).toHaveBeenCalledWith( + 'result2', + null, + 'todo', + undefined, + ) + }) + + it('should go to error state if onSuccess callback errors', async () => { + const error = new Error('error from onSuccess') + const onError = jest.fn() + + function Page() { + const mutation = createMutation( + async (_text: string) => { + await sleep(10) + return 'result' + }, + { + onSuccess: () => Promise.reject(error), + onError, + }, + ) + + return ( +
+ +
status: {mutation.status}
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('status: idle') + + screen.getByRole('button', { name: /mutate/i }).click() + + await screen.findByText('status: error') + + expect(onError).toHaveBeenCalledWith(error, 'todo', undefined) + }) + + it('should go to error state if onError callback errors', async () => { + const error = new Error('error from onError') + const mutateFnError = new Error('mutateFnError') + + function Page() { + const mutation = createMutation( + async (_text: string) => { + await sleep(10) + throw mutateFnError + }, + { + onError: () => Promise.reject(error), + }, + ) + + return ( +
+ +
+ error:{' '} + {mutation.error instanceof Error ? mutation.error.message : 'null'}, + status: {mutation.status} +
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('error: null, status: idle') + + screen.getByRole('button', { name: /mutate/i }).click() + + await screen.findByText('error: mutateFnError, status: error') + }) + + it('should go to error state if onSettled callback errors', async () => { + const error = new Error('error from onSettled') + const mutateFnError = new Error('mutateFnError') + const onError = jest.fn() + + function Page() { + const mutation = createMutation( + async (_text: string) => { + await sleep(10) + throw mutateFnError + }, + { + onSettled: () => Promise.reject(error), + onError, + }, + ) + + return ( +
+ +
+ error:{' '} + {mutation.error instanceof Error ? mutation.error.message : 'null'}, + status: {mutation.status} +
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('error: null, status: idle') + + screen.getByRole('button', { name: /mutate/i }).click() + + await screen.findByText('error: mutateFnError, status: error') + + expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) + }) +}) diff --git a/packages/solid-query/src/__tests__/createQueries.test.tsx b/packages/solid-query/src/__tests__/createQueries.test.tsx new file mode 100644 index 0000000000..836943a968 --- /dev/null +++ b/packages/solid-query/src/__tests__/createQueries.test.tsx @@ -0,0 +1,1163 @@ +import { fireEvent, render, screen, waitFor } from 'solid-testing-library' + +import * as QueriesObserverModule from '../../../query-core/src/queriesObserver' + +import type { QueryFunctionContext } from '@tanstack/query-core' +import { + createContext, + createMemo, + createRenderEffect, + createSignal, + ErrorBoundary, +} from 'solid-js' +import type { + CreateQueryOptions, + CreateQueryResult, + QueryClient, + QueryFunction, + QueryObserverResult, + SolidQueryKey, +} from '..' +import { + createQueries, + QueriesObserver, + QueryCache, + QueryClientProvider, +} from '..' +import { + createQueryClient, + expectType, + expectTypeNotAny, + queryKey, + sleep, +} from './utils' + +describe('useQueries', () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + it('should return the correct states', async () => { + const key1 = queryKey() + const key2 = queryKey() + const results: CreateQueryResult[][] = [] + + function Page() { + const result = createQueries({ + queries: [ + { + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 1 + }, + }, + { + queryKey: key2, + queryFn: async () => { + await sleep(100) + return 2 + }, + }, + ], + }) + + createRenderEffect(() => { + results.push([...result]) + }) + + return ( +
+
+ data1: {String(result[0].data ?? 'null')}, data2:{' '} + {String(result[1].data ?? 'null')} +
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data1: 1, data2: 2')) + + expect(results.length).toBe(3) + expect(results[0]).toMatchObject([{ data: undefined }, { data: undefined }]) + expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }]) + expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }]) + }) + + it('should keep previous data if amount of queries is the same', async () => { + const key1 = queryKey() + const key2 = queryKey() + const states: CreateQueryResult[][] = [] + + function Page() { + const [count, setCount] = createSignal(1) + const result = createQueries({ + queries: [ + { + queryKey: () => [key1(), count()], + keepPreviousData: true, + queryFn: async () => { + await sleep(10) + return count() * 2 + }, + }, + { + queryKey: () => [key2(), count()], + keepPreviousData: true, + queryFn: async () => { + await sleep(35) + return count() * 5 + }, + }, + ], + }) + + createRenderEffect(() => { + states.push([...result]) + }) + + const isFetching = createMemo(() => result.some((r) => r.isFetching)) + + return ( +
+
+ data1: {String(result[0].data ?? 'null')}, data2:{' '} + {String(result[1].data ?? 'null')} +
+
isFetching: {String(isFetching())}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data1: 2, data2: 5')) + fireEvent.click(screen.getByRole('button', { name: /inc/i })) + + await waitFor(() => screen.getByText('data1: 4, data2: 10')) + await waitFor(() => screen.getByText('isFetching: false')) + + expect(states[states.length - 1]).toMatchObject([ + { status: 'success', data: 4, isPreviousData: false, isFetching: false }, + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + ]) + }) + + it('should keep previous data for variable amounts of useQueries', async () => { + const key = queryKey() + const states: CreateQueryResult[][] = [] + + function Page() { + const [count, setCount] = createSignal(2) + const result = createQueries({ + // TODO(lukemurray): reactive queries doesn't appear to work + get queries() { + return Array.from({ length: count() }, (_, i) => ({ + queryKey: () => [key(), count(), i + 1], + keepPreviousData: true, + queryFn: async () => { + await sleep(35 * (i + 1)) + return (i + 1) * count() * 2 + }, + })) + }, + }) + + createRenderEffect(() => { + states.push([...result]) + }) + + const isFetching = createMemo(() => result.some((r) => r.isFetching)) + + return ( +
+
data: {result.map((it) => it.data).join(',')}
+
isFetching: {String(isFetching())}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 4,8')) + fireEvent.click(screen.getByRole('button', { name: /inc/i })) + + await waitFor(() => screen.getByText('data: 6,12,18')) + await waitFor(() => screen.getByText('isFetching: false')) + + expect(states[states.length - 1]).toMatchObject([ + { status: 'success', data: 6, isPreviousData: false, isFetching: false }, + { status: 'success', data: 12, isPreviousData: false, isFetching: false }, + { status: 'success', data: 18, isPreviousData: false, isFetching: false }, + ]) + }) + + it('should keep previous data when switching between queries', async () => { + const key = queryKey() + const states: CreateQueryResult[][] = [] + + function Page() { + const [series1, setSeries1] = createSignal(1) + const [series2, setSeries2] = createSignal(2) + const ids = [series1, series2] + + const result = createQueries({ + queries: ids.map((id) => { + return { + queryKey: () => [key(), id()], + queryFn: async () => { + await sleep(5) + return id() * 5 + }, + keepPreviousData: true, + } + }), + }) + + createRenderEffect(() => { + states.push([...result]) + }) + + const isFetching = createMemo(() => result.some((r) => r.isFetching)) + + return ( +
+
+ data1: {String(result[0]?.data ?? 'null')}, data2:{' '} + {String(result[1]?.data ?? 'null')} +
+
isFetching: {String(isFetching())}
+ + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data1: 5, data2: 10')) + fireEvent.click(screen.getByRole('button', { name: /setSeries2/i })) + + await waitFor(() => screen.getByText('data1: 5, data2: 15')) + fireEvent.click(screen.getByRole('button', { name: /setSeries1/i })) + + await waitFor(() => screen.getByText('data1: 10, data2: 15')) + await waitFor(() => screen.getByText('isFetching: false')) + + expect(states[states.length - 1]).toMatchObject([ + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + { status: 'success', data: 15, isPreviousData: false, isFetching: false }, + ]) + }) + + it('should not go to infinite render loop with previous data when toggling queries', async () => { + const key = queryKey() + const states: CreateQueryResult[][] = [] + + function Page() { + const [enableId1, setEnableId1] = createSignal(true) + const ids = createMemo(() => (enableId1() ? [1, 2] : [2])) + + const result = createQueries({ + // TODO(lukemurray): same issue queries should be reactive + get queries() { + return ids().map((id) => { + return { + queryKey: () => [key(), id], + queryFn: async () => { + await sleep(5) + return id * 5 + }, + keepPreviousData: true, + } + }) + }, + }) + + createRenderEffect(() => { + states.push([...result]) + }) + + const text = createMemo(() => { + return result + .map((r, idx) => `data${idx + 1}: ${r.data ?? 'null'}`) + .join(' ') + }) + + const isFetching = createMemo(() => result.some((r) => r.isFetching)) + + return ( +
+
{text()}
+
isFetching: {String(isFetching())}
+ + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data1: 5 data2: 10')) + fireEvent.click(screen.getByRole('button', { name: /set1Disabled/i })) + + await waitFor(() => screen.getByText('data1: 10')) + await waitFor(() => screen.getByText('isFetching: false')) + fireEvent.click(screen.getByRole('button', { name: /set2Enabled/i })) + + await waitFor(() => screen.getByText('data1: 5 data2: 10')) + await waitFor(() => screen.getByText('isFetching: false')) + + await waitFor(() => expect(states.length).toBe(5)) + + expect(states[0]).toMatchObject([ + { + status: 'loading', + data: undefined, + isPreviousData: false, + isFetching: true, + }, + { + status: 'loading', + data: undefined, + isPreviousData: false, + isFetching: true, + }, + ]) + expect(states[1]).toMatchObject([ + { status: 'success', data: 5, isPreviousData: false, isFetching: false }, + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + ]) + expect(states[2]).toMatchObject([ + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + ]) + expect(states[3]).toMatchObject([ + { status: 'success', data: 5, isPreviousData: false, isFetching: true }, + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + ]) + expect(states[4]).toMatchObject([ + { status: 'success', data: 5, isPreviousData: false, isFetching: false }, + { status: 'success', data: 10, isPreviousData: false, isFetching: false }, + ]) + }) + + it('handles type parameter - tuple of tuples', async () => { + const key1 = queryKey() + const key2 = queryKey() + const key3 = queryKey() + + // @ts-expect-error (Page component is not rendered) + // eslint-disable-next-line + function Page() { + const result1 = createQueries<[[number], [string], [string[], boolean]]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }) + expectType>(result1[0]) + expectType>(result1[1]) + expectType>(result1[2]) + expectType(result1[0].data) + expectType(result1[1].data) + expectType(result1[2].data) + expectType(result1[2].error) + + // TData (3rd element) takes precedence over TQueryFnData (1st element) + const result2 = createQueries< + [[string, unknown, string], [string, unknown, number]] + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + }, + ], + }) + expectType>(result2[0]) + expectType>(result2[1]) + expectType(result2[0].data) + expectType(result2[1].data) + + // types should be enforced + createQueries<[[string, unknown, string], [string, boolean, number]]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + onSuccess: (a) => { + expectType(a) + expectTypeNotAny(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + onSuccess: (a) => { + expectType(a) + expectTypeNotAny(a) + }, + onError: (e) => { + expectType(e) + expectTypeNotAny(e) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + ], + }) + + // field names should be enforced + createQueries<[[string]]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) + } + }) + + it('handles type parameter - tuple of objects', async () => { + const key1 = queryKey() + const key2 = queryKey() + const key3 = queryKey() + + // @ts-expect-error (Page component is not rendered) + // eslint-disable-next-line + function Page() { + const result1 = createQueries< + [ + { queryFnData: number }, + { queryFnData: string }, + { queryFnData: string[]; error: boolean }, + ] + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + }, + ], + }) + expectType>(result1[0]) + expectType>(result1[1]) + expectType>(result1[2]) + expectType(result1[0].data) + expectType(result1[1].data) + expectType(result1[2].data) + expectType(result1[2].error) + + // TData (data prop) takes precedence over TQueryFnData (queryFnData prop) + const result2 = createQueries< + [ + { queryFnData: string; data: string }, + { queryFnData: string; data: number }, + ] + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + }, + ], + }) + expectType>(result2[0]) + expectType>(result2[1]) + expectType(result2[0].data) + expectType(result2[1].data) + + // can pass only TData (data prop) although TQueryFnData will be left unknown + const result3 = createQueries<[{ data: string }, { data: number }]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a as string + }, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a as number + }, + }, + ], + }) + expectType>(result3[0]) + expectType>(result3[1]) + expectType(result3[0].data) + expectType(result3[1].data) + + // types should be enforced + createQueries< + [ + { queryFnData: string; data: string }, + { queryFnData: string; data: number; error: boolean }, + ] + >({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return a.toLowerCase() + }, + onSuccess: (a) => { + expectType(a) + expectTypeNotAny(a) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 'string', + select: (a) => { + expectType(a) + expectTypeNotAny(a) + return parseInt(a) + }, + onSuccess: (a) => { + expectType(a) + expectTypeNotAny(a) + }, + onError: (e) => { + expectType(e) + expectTypeNotAny(e) + }, + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + ], + }) + + // field names should be enforced + createQueries<[{ queryFnData: string }]>({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) + } + }) + + it('handles array literal without type parameter to infer result type', async () => { + const key1 = queryKey() + const key2 = queryKey() + const key3 = queryKey() + const key4 = queryKey() + + // @ts-expect-error (Page component is not rendered) + // eslint-disable-next-line + function Page() { + // Array.map preserves TQueryFnData + const result1 = createQueries({ + queries: Array(50).map((_, i) => ({ + queryKey: () => ['key', i] as const, + queryFn: () => i + 10, + })), + }) + expectType[]>(result1) + expectType(result1[0]?.data) + + // Array.map preserves TData + const result2 = createQueries({ + queries: Array(50).map((_, i) => ({ + queryKey: () => ['key', i] as const, + queryFn: () => i + 10, + select: (data: number) => data.toString(), + })), + }) + expectType[]>(result2) + + const result3 = createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 1, + }, + { + queryKey: key2, + queryFn: () => 'string', + }, + { + queryKey: key3, + queryFn: () => ['string[]'], + select: () => 123, + }, + ], + }) + expectType>(result3[0]) + expectType>(result3[1]) + expectType>(result3[2]) + expectType(result3[0].data) + expectType(result3[1].data) + // select takes precedence over queryFn + expectType(result3[2].data) + + // initialData/placeholderData are enforced + createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + placeholderData: 'string', + // @ts-expect-error (initialData: string) + initialData: 123, + }, + { + queryKey: key2, + queryFn: () => 123, + // @ts-expect-error (placeholderData: number) + placeholderData: 'string', + initialData: 123, + }, + ], + }) + + // select / onSuccess / onSettled params are "indirectly" enforced + createQueries({ + queries: [ + // unfortunately TS will not suggest the type for you + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (noImplicitAny) + onSuccess: (a) => null, + // @ts-expect-error (noImplicitAny) + onSettled: (a) => null, + }, + // however you can add a type to the callback + { + queryKey: key2, + queryFn: () => 'string', + onSuccess: (a: string) => { + expectType(a) + expectTypeNotAny(a) + }, + onSettled: (a: string | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + // the type you do pass is enforced + { + queryKey: key3, + queryFn: () => 'string', + // @ts-expect-error (only accepts string) + onSuccess: (a: number) => null, + }, + { + queryKey: key4, + queryFn: () => 'string', + select: (a: string) => parseInt(a), + // @ts-expect-error (select is defined => only accepts number) + onSuccess: (a: string) => null, + onSettled: (a: number | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + ], + }) + + // callbacks are also indirectly enforced with Array.map + createQueries({ + // @ts-expect-error (onSuccess only accepts string) + queries: Array(50).map((_, i) => ({ + queryKey: ['key', i] as const, + queryFn: () => i + 10, + select: (data: number) => data.toString(), + onSuccess: (_data: number) => null, + })), + }) + createQueries({ + queries: Array(50).map((_, i) => ({ + queryKey: () => ['key', i] as const, + queryFn: () => i + 10, + select: (data: number) => data.toString(), + onSuccess: (_data: string) => null, + })), + }) + + // results inference works when all the handlers are defined + const result4 = createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (noImplicitAny) + onSuccess: (a) => null, + // @ts-expect-error (noImplicitAny) + onSettled: (a) => null, + }, + { + queryKey: key2, + queryFn: () => 'string', + onSuccess: (a: string) => { + expectType(a) + expectTypeNotAny(a) + }, + onSettled: (a: string | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + { + queryKey: key4, + queryFn: () => 'string', + select: (a: string) => parseInt(a), + onSuccess: (_a: number) => null, + onSettled: (a: number | undefined) => { + expectType(a) + expectTypeNotAny(a) + }, + }, + ], + }) + expectType>(result4[0]) + expectType>(result4[1]) + expectType>(result4[2]) + + // handles when queryFn returns a Promise + const result5 = createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => Promise.resolve('string'), + onSuccess: (a: string) => { + expectType(a) + expectTypeNotAny(a) + }, + // @ts-expect-error (refuses to accept a Promise) + onSettled: (a: Promise) => null, + }, + ], + }) + expectType>(result5[0]) + + // Array as const does not throw error + const result6 = createQueries({ + queries: [ + { + queryKey: () => ['key1'], + queryFn: () => 'string', + }, + { + queryKey: () => ['key1'], + queryFn: () => 123, + }, + ], + } as const) + expectType>(result6[0]) + expectType>(result6[1]) + + // field names should be enforced - array literal + createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) + + // field names should be enforced - Array.map() result + createQueries({ + // @ts-expect-error (invalidField) + queries: Array(10).map(() => ({ + someInvalidField: '', + })), + }) + + // field names should be enforced - array literal + createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => 'string', + // @ts-expect-error (invalidField) + someInvalidField: [], + }, + ], + }) + + // supports queryFn using fetch() to return Promise - Array.map() result + createQueries({ + queries: Array(50).map((_, i) => ({ + queryKey: () => ['key', i] as const, + queryFn: () => + fetch('return Promise').then((resp) => resp.json()), + })), + }) + + // supports queryFn using fetch() to return Promise - array literal + createQueries({ + queries: [ + { + queryKey: key1, + queryFn: () => + fetch('return Promise').then((resp) => resp.json()), + }, + ], + }) + } + }) + + it('handles strongly typed queryFn factories and useQueries wrappers', () => { + // QueryKey + queryFn factory + type QueryKeyA = ['queryA'] + const getQueryKeyA = (): QueryKeyA => ['queryA'] + type GetQueryFunctionA = () => QueryFunction + const getQueryFunctionA: GetQueryFunctionA = () => async () => { + return 1 + } + type SelectorA = (data: number) => [number, string] + const getSelectorA = (): SelectorA => (data) => [data, data.toString()] + + type QueryKeyB = ['queryB', string] + const getQueryKeyB = (id: string): QueryKeyB => ['queryB', id] + type GetQueryFunctionB = () => QueryFunction + const getQueryFunctionB: GetQueryFunctionB = () => async () => { + return '1' + } + type SelectorB = (data: string) => [string, number] + const getSelectorB = (): SelectorB => (data) => [data, +data] + + // Wrapper with strongly typed array-parameter + function useWrappedQueries< + TQueryFnData, + TError, + TData, + TQueryKey extends SolidQueryKey, + >(queries: CreateQueryOptions[]) { + return createQueries({ + queries: queries.map( + // no need to type the mapped query + (query) => { + const { queryFn: fn, queryKey: key, onError: err } = query + expectType< + QueryFunction> | undefined + >(fn) + return { + queryKey: key, + onError: err, + queryFn: fn + ? (ctx: QueryFunctionContext>) => { + expectType>(ctx.queryKey) + return fn.call({}, ctx) + } + : undefined, + } + }, + ), + }) + } + + // @ts-expect-error (Page component is not rendered) + // eslint-disable-next-line + function Page() { + const result = createQueries({ + queries: [ + { + queryKey: () => getQueryKeyA(), + queryFn: getQueryFunctionA(), + }, + { + queryKey: () => getQueryKeyB('id'), + queryFn: getQueryFunctionB(), + }, + ], + }) + expectType>(result[0]) + expectType>(result[1]) + + const withSelector = createQueries({ + queries: [ + { + queryKey: () => getQueryKeyA(), + queryFn: getQueryFunctionA(), + select: getSelectorA(), + }, + { + queryKey: () => getQueryKeyB('id'), + queryFn: getQueryFunctionB(), + select: getSelectorB(), + }, + ], + }) + expectType>( + withSelector[0], + ) + expectType>( + withSelector[1], + ) + + const withWrappedQueries = useWrappedQueries( + Array(10).map(() => ({ + queryKey: () => getQueryKeyA(), + queryFn: getQueryFunctionA(), + select: getSelectorA(), + })), + ) + + expectType[]>( + withWrappedQueries, + ) + } + }) + + it('should not change state if unmounted', async () => { + const key1 = queryKey() + + // We have to mock the QueriesObserver to not unsubscribe + // the listener when the component is unmounted + class QueriesObserverMock extends QueriesObserver { + subscribe(listener: any) { + super.subscribe(listener) + return () => void 0 + } + } + + const QueriesObserverSpy = jest + .spyOn(QueriesObserverModule, 'QueriesObserver') + .mockImplementation((fn) => { + return new QueriesObserverMock(fn) + }) + + function Queries() { + createQueries({ + queries: [ + { + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 1 + }, + }, + ], + }) + + return ( +
+ queries +
+ ) + } + + function Page() { + const [mounted, setMounted] = createSignal(true) + + return ( +
+ + {mounted() && } +
+ ) + } + + render(() => ( + + + + )) + fireEvent.click(screen.getByText('unmount')) + + // Should not display the console error + // "Warning: Can't perform a React state update on an unmounted component" + + await sleep(20) + QueriesObserverSpy.mockRestore() + }) + + describe('with custom context', () => { + it('should return the correct states', async () => { + const context = createContext(undefined) + + const key1 = queryKey() + const key2 = queryKey() + const results: CreateQueryResult[][] = [] + + function Page() { + const result = createQueries({ + context, + queries: [ + { + queryKey: key1, + queryFn: async () => { + await sleep(5) + return 1 + }, + }, + { + queryKey: key2, + queryFn: async () => { + await sleep(10) + return 2 + }, + }, + ], + }) + createRenderEffect(() => { + results.push([...result]) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(30) + + expect(results[0]).toMatchObject([ + { data: undefined }, + { data: undefined }, + ]) + expect(results[results.length - 1]).toMatchObject([ + { data: 1 }, + { data: 2 }, + ]) + }) + + it('should throw if the context is necessary and is not passed to useQueries', async () => { + const context = createContext(undefined) + + const key1 = queryKey() + const key2 = queryKey() + const results: CreateQueryResult[][] = [] + + function Page() { + const result = createQueries({ + queries: [ + { + queryKey: key1, + queryFn: async () => 1, + }, + { + queryKey: key2, + queryFn: async () => 2, + }, + ], + }) + results.push(result) + return null + } + + render(() => ( + +
error boundary
}> + +
+
+ )) + + await waitFor(() => screen.getByText('error boundary')) + }) + }) +}) diff --git a/packages/solid-query/src/__tests__/createQuery.test.tsx b/packages/solid-query/src/__tests__/createQuery.test.tsx new file mode 100644 index 0000000000..9437169cb8 --- /dev/null +++ b/packages/solid-query/src/__tests__/createQuery.test.tsx @@ -0,0 +1,6487 @@ +import '@testing-library/jest-dom' +import type { JSX } from 'solid-js' +import { + createEffect, + createMemo, + createRenderEffect, + createSignal, + ErrorBoundary, + Match, + on, + Switch, +} from 'solid-js' +import { fireEvent, render, screen, waitFor } from 'solid-testing-library' +import type { + CreateQueryOptions, + CreateQueryResult, + DefinedCreateQueryResult, + QueryFunction, +} from '..' +import { createQuery, QueryCache, QueryClientProvider } from '..' +import { + Blink, + createQueryClient, + expectType, + mockLogger, + mockNavigatorOnLine, + mockVisibilityState, + queryKey, + setActTimeout, + sleep, +} from './utils' + +describe('createQuery', () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + it('should return the correct types', () => { + const key = queryKey() + + // @ts-ignore + // eslint-disable-next-line + function Page() { + // unspecified query function should default to unknown + const noQueryFn = createQuery(key) + expectType(noQueryFn.data) + expectType(noQueryFn.error) + + // it should infer the result type from the query function + const fromQueryFn = createQuery(key, () => 'test') + expectType(fromQueryFn.data) + expectType(fromQueryFn.error) + + // it should be possible to specify the result type + const withResult = createQuery(key, () => 'test') + expectType(withResult.data) + expectType(withResult.error) + + // it should be possible to specify the error type + const withError = createQuery(key, () => 'test') + expectType(withError.data) + expectType(withError.error) + + // it should provide the result type in the configuration + createQuery( + () => [key()], + async () => true, + { + onSuccess: (data) => expectType(data), + onSettled: (data) => expectType(data), + }, + ) + + // it should be possible to specify a union type as result type + const unionTypeSync = createQuery( + key, + () => (Math.random() > 0.5 ? 'a' : 'b'), + { + onSuccess: (data) => expectType<'a' | 'b'>(data), + }, + ) + expectType<'a' | 'b' | undefined>(unionTypeSync.data) + const unionTypeAsync = createQuery<'a' | 'b'>( + key, + () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), + { + onSuccess: (data) => expectType<'a' | 'b'>(data), + }, + ) + expectType<'a' | 'b' | undefined>(unionTypeAsync.data) + + // should error when the query function result does not match with the specified type + // @ts-expect-error + createQuery(key, () => 'test') + + // it should infer the result type from a generic query function + function queryFn(): Promise { + return Promise.resolve({} as T) + } + + const fromGenericQueryFn = createQuery(key, () => queryFn()) + expectType(fromGenericQueryFn.data) + expectType(fromGenericQueryFn.error) + + const fromGenericOptionsQueryFn = createQuery({ + queryKey: key, + queryFn: () => queryFn(), + }) + expectType(fromGenericOptionsQueryFn.data) + expectType(fromGenericOptionsQueryFn.error) + + type MyData = number + type MyQueryKey = readonly ['my-data', number] + + const getMyDataArrayKey: QueryFunction = async ({ + queryKey: [, n], + }) => { + return n + 42 + } + + createQuery({ + queryKey: () => ['my-data', 100] as const, + queryFn: getMyDataArrayKey, + }) + + const getMyDataStringKey: QueryFunction = async ( + context, + ) => { + expectType(context.queryKey) + return Number(context.queryKey[0]) + 42 + } + + createQuery({ + queryKey: () => ['1'] as const, + queryFn: getMyDataStringKey, + }) + + // it should handle query-functions that return Promise + createQuery(key, () => + fetch('return Promise').then((resp) => resp.json()), + ) + + // handles wrapped queries with custom fetcher passed as inline queryFn + const useWrappedQuery = < + TQueryKey extends () => [string, Record?], + TQueryFnData, + TError, + TData = TQueryFnData, + >( + qk: TQueryKey, + fetcher: ( + obj: ReturnType[1], + token: string, + // return type must be wrapped with TQueryFnReturn + ) => Promise, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'queryFn' | 'initialData' + >, + ) => createQuery(qk, () => fetcher(qk()[1], 'token'), options) + const test = useWrappedQuery( + () => [''], + async () => '1', + ) + expectType(test.data) + + // handles wrapped queries with custom fetcher passed directly to createQuery + const useWrappedFuncStyleQuery = < + TQueryKey extends () => [string, Record?], + TQueryFnData, + TError, + TData = TQueryFnData, + >( + qk: TQueryKey, + fetcher: () => Promise, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'queryFn' | 'initialData' + >, + ) => createQuery(qk, fetcher, options) + const testFuncStyle = useWrappedFuncStyleQuery( + () => [''], + async () => true, + ) + expectType(testFuncStyle.data) + } + }) + + // See https://github.com/tannerlinsley/react-query/issues/105 + it('should allow to set default data value', async () => { + const key = queryKey() + + function Page() { + const state = createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + return ( +
+

{state.data ?? 'default'}

+
+ ) + } + + render(() => ( + + + + )) + + screen.getByText('default') + + await waitFor(() => screen.getByText('test')) + }) + + it('should return the correct states for a successful query', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page(): JSX.Element { + const state = createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + if (state.isLoading) { + expectType(state.data) + expectType(state.error) + } else if (state.isLoadingError) { + expectType(state.data) + expectType(state.error) + } else { + expectType(state.data) + expectType(state.error) + } + + return ( + {state.data}}> + + loading + + + {state.error!.message} + + + ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('test')) + + expect(states.length).toEqual(2) + + expect(states[0]).toEqual({ + data: undefined, + dataUpdatedAt: 0, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isError: false, + isFetched: false, + isFetchedAfterMount: false, + isFetching: true, + isPaused: false, + isLoading: true, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: false, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'loading', + fetchStatus: 'fetching', + }) + + expect(states[1]).toEqual({ + data: 'test', + dataUpdatedAt: expect.any(Number), + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isError: false, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isPaused: false, + isLoading: false, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: true, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'success', + fetchStatus: 'idle', + }) + }) + + it('should return the correct states for an unsuccessful query', async () => { + const key = queryKey() + + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery( + key, + () => Promise.reject('rejected'), + { + retry: 1, + retryDelay: 1, + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+

Status: {state.status}

+
Failure Count: {state.failureCount}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Status: error')) + + expect(states[0]).toEqual({ + data: undefined, + dataUpdatedAt: 0, + error: null, + errorUpdatedAt: 0, + failureCount: 0, + errorUpdateCount: 0, + isError: false, + isFetched: false, + isFetchedAfterMount: false, + isFetching: true, + isPaused: false, + isLoading: true, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: false, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'loading', + fetchStatus: 'fetching', + }) + + expect(states[1]).toEqual({ + data: undefined, + dataUpdatedAt: 0, + error: null, + errorUpdatedAt: 0, + failureCount: 1, + errorUpdateCount: 0, + isError: false, + isFetched: false, + isFetchedAfterMount: false, + isFetching: true, + isPaused: false, + isLoading: true, + isLoadingError: false, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: false, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'loading', + fetchStatus: 'fetching', + }) + + expect(states[2]).toEqual({ + data: undefined, + dataUpdatedAt: 0, + error: 'rejected', + errorUpdatedAt: expect.any(Number), + failureCount: 2, + errorUpdateCount: 1, + isError: true, + isFetched: true, + isFetchedAfterMount: true, + isFetching: false, + isPaused: false, + isLoading: false, + isLoadingError: true, + isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, + isRefetching: false, + isStale: true, + isSuccess: false, + refetch: expect.any(Function), + remove: expect.any(Function), + status: 'error', + fetchStatus: 'idle', + }) + }) + + it('should set isFetchedAfterMount to true after a query has been fetched', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + // TODO(lukemurray): do we want reactivity on this key? + await queryClient.prefetchQuery(key(), () => 'prefetched') + + function Page() { + const state = createQuery(key, () => 'data') + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + expect(states.length).toBe(2) + + expect(states[0]).toMatchObject({ + data: 'prefetched', + isFetched: true, + isFetchedAfterMount: false, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isFetched: true, + isFetchedAfterMount: true, + }) + }) + + it('should call onSuccess after a query has been fetched', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onSuccess = jest.fn() + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + return 'data' + }, + { onSuccess }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return
data: {state.data}
+ } + + render(() => ( + + + + )) + + await screen.findByText('data: data') + expect(states.length).toBe(2) + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(onSuccess).toHaveBeenCalledWith('data') + }) + + it('should call onSuccess after a query has been refetched', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onSuccess = jest.fn() + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + count++ + await sleep(10) + return 'data' + count + }, + { onSuccess }, + ) + + createRenderEffect( + on( + () => [state.data, state.refetch], + () => { + states.push(state) + }, + ), + ) + + return ( +
+
data: {state.data}
+ +
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('data: data1') + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + await screen.findByText('data: data2') + + expect(states.length).toBe(3) //loading, success, success after refetch + expect(count).toBe(2) + expect(onSuccess).toHaveBeenCalledTimes(2) + }) + + it('should call onSuccess after a disabled query has been fetched', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onSuccess = jest.fn() + + function Page() { + const state = createQuery(key, () => 'data', { + enabled: false, + onSuccess, + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const refetch = state.refetch + setActTimeout(() => { + refetch() + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(50) + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(onSuccess).toHaveBeenCalledWith('data') + }) + + it('should not call onSuccess if a component has unmounted', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onSuccess = jest.fn() + + function Page() { + const [show, setShow] = createSignal(true) + + createEffect(() => { + setShow(false) + }) + return <>{show() && } + } + + function Component() { + const state = createQuery( + key, + async () => { + await sleep(10) + return 'data' + }, + { onSuccess }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(50) + expect(states.length).toBe(1) + expect(onSuccess).toHaveBeenCalledTimes(0) + }) + + it('should call onError after a query has been fetched with an error', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onError = jest.fn() + + function Page() { + const state = createQuery(key, () => Promise.reject('error'), { + retry: false, + onError, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(10) + expect(states.length).toBe(2) + expect(onError).toHaveBeenCalledTimes(1) + expect(onError).toHaveBeenCalledWith('error') + }) + + it('should not call onError when receiving a CancelledError', async () => { + const key = queryKey() + const onError = jest.fn() + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + return 23 + }, + { + onError, + }, + ) + return ( + + status: {state.status}, fetchStatus: {state.fetchStatus} + + ) + } + + render(() => ( + + + + )) + + await sleep(5) + await queryClient.cancelQueries(key()) + // query cancellation will reset the query to it's initial state + await waitFor(() => screen.getByText('status: loading, fetchStatus: idle')) + expect(onError).not.toHaveBeenCalled() + }) + + it('should call onSettled after a query has been fetched', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onSettled = jest.fn() + + function Page() { + const state = createQuery(key, () => 'data', { onSettled }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + expect(states.length).toBe(2) + expect(onSettled).toHaveBeenCalledTimes(1) + expect(onSettled).toHaveBeenCalledWith('data', null) + }) + + it('should call onSettled after a query has been fetched with an error', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const onSettled = jest.fn() + + function Page() { + const state = createQuery(key, () => Promise.reject('error'), { + retry: false, + onSettled, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + expect(states.length).toBe(2) + expect(onSettled).toHaveBeenCalledTimes(1) + expect(onSettled).toHaveBeenCalledWith(undefined, 'error') + }) + + it('should not cancel an ongoing fetch when refetch is called with cancelRefetch=false if we have data already', async () => { + const key = queryKey() + let fetchCount = 0 + + function Page() { + const state = createQuery( + key, + async () => { + fetchCount++ + await sleep(10) + return 'data' + }, + { enabled: false, initialData: 'initialData' }, + ) + + createEffect(() => { + setActTimeout(() => { + state.refetch() + }, 5) + setActTimeout(() => { + state.refetch({ cancelRefetch: false }) + }, 5) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(20) + // first refetch only, second refetch is ignored + expect(fetchCount).toBe(1) + }) + + it('should cancel an ongoing fetch when refetch is called (cancelRefetch=true) if we have data already', async () => { + const key = queryKey() + let fetchCount = 0 + + function Page() { + const state = createQuery( + key, + async () => { + fetchCount++ + await sleep(10) + return 'data' + }, + { enabled: false, initialData: 'initialData' }, + ) + + createEffect(() => { + setActTimeout(() => { + state.refetch() + }, 5) + setActTimeout(() => { + state.refetch() + }, 5) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(20) + // first refetch (gets cancelled) and second refetch + expect(fetchCount).toBe(2) + }) + + it('should not cancel an ongoing fetch when refetch is called (cancelRefetch=true) if we do not have data yet', async () => { + const key = queryKey() + let fetchCount = 0 + + function Page() { + const state = createQuery( + key, + async () => { + fetchCount++ + await sleep(10) + return 'data' + }, + { enabled: false }, + ) + + createEffect(() => { + setActTimeout(() => { + state.refetch() + }, 5) + setActTimeout(() => { + state.refetch() + }, 5) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(20) + // first refetch will not get cancelled, second one gets skipped + expect(fetchCount).toBe(1) + }) + + it('should be able to watch a query without providing a query function', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + // TODO(lukemurray): do we want this to be reactive. + queryClient.setQueryDefaults(key(), { queryFn: () => 'data' }) + + function Page() { + const state = createQuery(key) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'data' }) + }) + + it('should pick up a query when re-mounting with cacheTime 0', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const [toggle, setToggle] = createSignal(false) + + return ( +
+ + + + + + + + + +
+ ) + } + + function Component({ value }: { value: string }) { + const state = createQuery( + key, + async () => { + await sleep(10) + return 'data: ' + value + }, + { + cacheTime: 0, + notifyOnChangeProps: 'all', + }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return ( +
+
{state.data}
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('data: 1') + + fireEvent.click(screen.getByRole('button', { name: /toggle/i })) + + await screen.findByText('data: 2') + + expect(states.length).toBe(4) + // First load + expect(states[0]).toMatchObject({ + isLoading: true, + isSuccess: false, + isFetching: true, + }) + // First success + expect(states[1]).toMatchObject({ + isLoading: false, + isSuccess: true, + isFetching: false, + }) + // Switch, goes to fetching + expect(states[2]).toMatchObject({ + isLoading: false, + isSuccess: true, + isFetching: true, + }) + // Second success + expect(states[3]).toMatchObject({ + isLoading: false, + isSuccess: true, + isFetching: false, + }) + }) + + it.skip('should not get into an infinite loop when removing a query with cacheTime 0 and rerendering', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + //@ts-expect-error -- skip this test + const [, rerender] = NotReact.useState({}) + + const state = createQuery( + key, + async () => { + await sleep(5) + return 'data' + }, + { + cacheTime: 0, + notifyOnChangeProps: 'all', + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + const { remove } = state + + //@ts-expect-error skip this test + NotReact.useEffect(() => { + setActTimeout(() => { + remove() + rerender({}) + }, 20) + }, [remove]) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(5) + // First load + expect(states[0]).toMatchObject({ isLoading: true, isSuccess: false }) + // First success + expect(states[1]).toMatchObject({ isLoading: false, isSuccess: true }) + // Remove + expect(states[2]).toMatchObject({ isLoading: true, isSuccess: false }) + // Hook state update + expect(states[3]).toMatchObject({ isLoading: true, isSuccess: false }) + // Second success + expect(states[4]).toMatchObject({ isLoading: false, isSuccess: true }) + }) + + it('should fetch when refetchOnMount is false and nothing has been fetched yet', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 'test', { + refetchOnMount: false, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it('should not fetch when refetchOnMount is false and data has been fetched already', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + queryClient.setQueryData(key(), 'prefetched') + + function Page() { + const state = createQuery(key, () => 'test', { + refetchOnMount: false, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(1) + expect(states[0]).toMatchObject({ data: 'prefetched' }) + }) + + it('should be able to select a part of the data with select', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => ({ name: 'test' }), { + select: (data) => data.name, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it('should be able to select a part of the data with select in object syntax', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: () => ({ name: 'test' }), + select: (data) => data.name, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it('should not re-render when it should only re-render only data change and the selected data did not change', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => ({ name: 'test' }), { + select: (data) => data.name, + notifyOnChangeProps: ['data'], + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const refetch = state.refetch + setActTimeout(() => { + refetch() + }, 5) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it('should throw an error when a selector throws', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const error = new Error('Select Error') + + function Page() { + const state = createQuery(key, () => ({ name: 'test' }), { + select: () => { + throw error + }, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(mockLogger.error).toHaveBeenCalledWith(error) + expect(states.length).toBe(2) + + expect(states[0]).toMatchObject({ status: 'loading', data: undefined }) + expect(states[1]).toMatchObject({ status: 'error', error }) + }) + + it.skip('should not re-run a stable select when it re-renders if selector throws an error', async () => { + const key = queryKey() + const error = new Error('Select Error') + let runs = 0 + + function Page() { + //@ts-expect-error -- we skip this test, and no such thing as rerender in solid + const [, rerender] = NotReact.useReducer(() => ({}), {}) + const state = createQuery( + key, + () => (runs === 0 ? 'test' : 'test2'), + { + select: () => { + runs++ + throw error + }, + }, + ) + return ( +
+
error: {state.error?.message}
+ + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('error: Select Error')) + expect(runs).toEqual(1) + fireEvent.click(screen.getByRole('button', { name: 'rerender' })) + await sleep(10) + expect(runs).toEqual(1) + fireEvent.click(screen.getByRole('button', { name: 'refetch' })) + await sleep(10) + expect(runs).toEqual(2) + }) + + it('should track properties and only re-render when a tracked property changes', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const data = state.data + const refetch = state.refetch + setActTimeout(() => { + if (data) { + refetch() + } + }, 20) + }) + + return ( +
+

{state.data ?? null}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('test')) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it('should always re-render if we are tracking props but not using any', async () => { + const key = queryKey() + let renderCount = 0 + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 'test') + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect( + on( + () => ({ ...state }), + () => { + renderCount++ + }, + ), + ) + + return ( +
+

hello

+
+ ) + } + + render(() => ( + + + + )) + + await sleep(10) + expect(renderCount).toBe(2) + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined }) + expect(states[1]).toMatchObject({ data: 'test' }) + }) + + it.skip('should be able to remove a query', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + //@ts-expect-error -- we skip this test, and no such thing as rerender in solid + const [, rerender] = NotReact.useState({}) + const state = createQuery(key, () => ++count, { + notifyOnChangeProps: 'all', + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + const { remove } = state + + return ( +
+ + + data: {state.data ?? 'null'} +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByRole('button', { name: /remove/i })) + + await sleep(20) + fireEvent.click(screen.getByRole('button', { name: /rerender/i })) + await waitFor(() => screen.getByText('data: 2')) + + expect(states.length).toBe(4) + // Initial + expect(states[0]).toMatchObject({ status: 'loading', data: undefined }) + // Fetched + expect(states[1]).toMatchObject({ status: 'success', data: 1 }) + // Remove + Hook state update, batched + expect(states[2]).toMatchObject({ status: 'loading', data: undefined }) + // Fetched + expect(states[3]).toMatchObject({ status: 'success', data: 2 }) + }) + + it('should create a new query when refetching a removed query', async () => { + const key = queryKey() + const states: Partial>[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + return ++count + }, + { notifyOnChangeProps: 'all' }, + ) + + createRenderEffect(() => { + states.push({ data: state.data, dataUpdatedAt: state.dataUpdatedAt }) + }) + + return ( +
+ + + data: {state.data ?? 'null'} +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByRole('button', { name: /remove/i })) + + await sleep(50) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + await waitFor(() => screen.getByText('data: 2')) + + expect(states.length).toBe(4) + // Initial + expect(states[0]).toMatchObject({ data: undefined, dataUpdatedAt: 0 }) + // Fetched + expect(states[1]).toMatchObject({ data: 1 }) + // Switch + expect(states[2]).toMatchObject({ data: undefined, dataUpdatedAt: 0 }) + // Fetched + expect(states[3]).toMatchObject({ data: 2 }) + }) + + it('should share equal data structures between query results', async () => { + const key = queryKey() + + const result1 = [ + { id: '1', done: false }, + { id: '2', done: false }, + ] + + const result2 = [ + { id: '1', done: false }, + { id: '2', done: true }, + ] + + const states: CreateQueryResult[] = [] + + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + count++ + return count === 1 ? result1 : result2 + }, + { notifyOnChangeProps: 'all' }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + const { refetch } = state + + return ( +
+ + data: {String(state.data?.[1]?.done)} +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: false')) + await sleep(20) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + await waitFor(() => screen.getByText('data: true')) + + await waitFor(() => expect(states.length).toBe(4)) + + const todos = states[2]?.data + const todo1 = todos?.[0] + const todo2 = todos?.[1] + + const newTodos = states[3]?.data + const newTodo1 = newTodos?.[0] + const newTodo2 = newTodos?.[1] + + expect(todos).toEqual(result1) + expect(newTodos).toEqual(result2) + expect(newTodos).not.toBe(todos) + expect(newTodo1).toBe(todo1) + expect(newTodo2).not.toBe(todo2) + + return null + }) + + it('should use query function from hook when the existing query does not have a query function', async () => { + const key = queryKey() + const results: Partial>[] = [] + + queryClient.setQueryData(key(), 'set') + + function Page() { + const result = createQuery( + key, + async () => { + await sleep(10) + return 'fetched' + }, + { + initialData: 'initial', + staleTime: Infinity, + }, + ) + + createRenderEffect(() => { + results.push({ data: result.data, isFetching: result.isFetching }) + }) + + return ( +
+
isFetching: {result.isFetching}
+ + data: {result.data} +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: set')) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + await waitFor(() => screen.getByText('data: fetched')) + + await waitFor(() => expect(results.length).toBe(3)) + + expect(results[0]).toMatchObject({ data: 'set', isFetching: false }) + expect(results[1]).toMatchObject({ data: 'set', isFetching: true }) + expect(results[2]).toMatchObject({ data: 'fetched', isFetching: false }) + }) + + it('should update query stale state and refetch when invalidated with invalidateQueries', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + count++ + return count + }, + { staleTime: Infinity, notifyOnChangeProps: 'all' }, + ) + + createEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + data: {state.data} +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + await waitFor(() => screen.getByText('data: 2')) + + await waitFor(() => expect(states.length).toBe(4)) + + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isRefetching: false, + isSuccess: false, + isStale: true, + }) + expect(states[1]).toMatchObject({ + data: 1, + isFetching: false, + isRefetching: false, + isSuccess: true, + isStale: false, + }) + expect(states[2]).toMatchObject({ + data: 1, + isFetching: true, + isRefetching: true, + isSuccess: true, + isStale: true, + }) + expect(states[3]).toMatchObject({ + data: 2, + isFetching: false, + isRefetching: false, + isSuccess: true, + isStale: false, + }) + }) + + it('should not update disabled query when refetched with refetchQueries', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + count++ + return count + }, + { enabled: false }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + setActTimeout(() => { + queryClient.refetchQueries({ queryKey: key() }) + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(50) + + expect(states.length).toBe(1) + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: false, + isSuccess: false, + isStale: true, + }) + }) + + it('should not refetch disabled query when invalidated with invalidateQueries', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + count++ + return count + }, + { enabled: false }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + setActTimeout(() => { + queryClient.invalidateQueries(key()) + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(1) + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: false, + isSuccess: false, + isStale: true, + }) + }) + + it('should not fetch when switching to a disabled query', async () => { + const key = queryKey() + const states: Partial>[] = [] + + function Page() { + const [count, setCount] = createSignal(0) + + const state = createQuery( + () => [key(), count()], + async () => { + await sleep(5) + return count() + }, + { + get enabled() { + return count() === 0 + }, + }, + ) + + createRenderEffect(() => { + const { data, isSuccess, isFetching } = state + states.push({ + data, + isFetching, + isSuccess, + }) + }) + + createEffect(() => { + setActTimeout(() => { + setCount(1) + }, 10) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(50) + + expect(states.length).toBe(3) + + // Fetch query + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isSuccess: false, + }) + // Fetched query + expect(states[1]).toMatchObject({ + data: 0, + isFetching: false, + isSuccess: true, + }) + // Switch to disabled query + expect(states[2]).toMatchObject({ + data: undefined, + isFetching: false, + isSuccess: false, + }) + }) + + it('should keep the previous data when keepPreviousData is set', async () => { + const key = queryKey() + const states: Partial>[] = [] + + function Page() { + const [count, setCount] = createSignal(0) + + const state = createQuery( + () => [key(), count()], + async () => { + await sleep(10) + return count() + }, + { keepPreviousData: true }, + ) + + createRenderEffect(() => { + const { data, isFetching, isSuccess, isPreviousData } = state + states.push({ + data, + isFetching, + isSuccess, + isPreviousData, + }) + }) + + createEffect(() => { + setActTimeout(() => { + setCount(1) + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await waitFor(() => expect(states.length).toBe(4)) + + // Initial + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + isSuccess: false, + isPreviousData: false, + }) + // Fetched + expect(states[1]).toMatchObject({ + data: 0, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + // Set state + expect(states[2]).toMatchObject({ + data: 0, + isFetching: true, + isSuccess: true, + isPreviousData: true, + }) + // New data + expect(states[3]).toMatchObject({ + data: 1, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + }) + + // this test relies on rerenders which don't exist in solid + it.skip('should transition to error state when keepPreviousData is set', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page(props: { count: number }) { + const state = createQuery( + () => [key(), props.count], + async () => { + await sleep(10) + if (props.count === 2) { + throw new Error('Error test') + } + return Promise.resolve(props.count) + }, + { + retry: false, + keepPreviousData: true, + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+

data: {state.data}

+

error: {state.error?.message}

+

previous data: {state.isPreviousData}

+
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('data: 0')) + // @ts-expect-error we skip this test and rerenders don't exist in solid + act(() => screen.rerender()) + await waitFor(() => screen.getByText('data: 1')) + // @ts-expect-error we skip this test and rerenders don't exist in solid + act(() => screen.rerender()) + await waitFor(() => screen.getByText('error: Error test')) + + await waitFor(() => expect(states.length).toBe(8)) + // Initial + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: true, + status: 'loading', + error: null, + isPreviousData: false, + }) + // Fetched + expect(states[1]).toMatchObject({ + data: 0, + isFetching: false, + status: 'success', + error: null, + isPreviousData: false, + }) + // rerender Page 1 + expect(states[2]).toMatchObject({ + data: 0, + isFetching: true, + status: 'success', + error: null, + isPreviousData: true, + }) + // Hook state update + expect(states[3]).toMatchObject({ + data: 0, + isFetching: true, + status: 'success', + error: null, + isPreviousData: true, + }) + // New data + expect(states[4]).toMatchObject({ + data: 1, + isFetching: false, + status: 'success', + error: null, + isPreviousData: false, + }) + // rerender Page 2 + expect(states[5]).toMatchObject({ + data: 1, + isFetching: true, + status: 'success', + error: null, + isPreviousData: true, + }) + // Hook state update again + expect(states[6]).toMatchObject({ + data: 1, + isFetching: true, + status: 'success', + error: null, + isPreviousData: true, + }) + // Error + expect(states[7]).toMatchObject({ + data: undefined, + isFetching: false, + status: 'error', + isPreviousData: false, + }) + expect(states[7]?.error).toHaveProperty('message', 'Error test') + }) + + it('should not show initial data from next query if keepPreviousData is set', async () => { + const key = queryKey() + const states: DefinedCreateQueryResult[] = [] + + function Page() { + const [count, setCount] = createSignal(0) + + const state = createQuery( + () => [key(), count()], + async () => { + await sleep(10) + return count() + }, + { initialData: 99, keepPreviousData: true }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + setActTimeout(() => { + setCount(1) + }, 20) + }) + + return null + } + + render(() => ( + + + + )) + + await waitFor(() => expect(states.length).toBe(4)) + + // Initial + expect(states[0]).toMatchObject({ + data: 99, + isFetching: true, + isSuccess: true, + isPreviousData: false, + }) + // Fetched + expect(states[1]).toMatchObject({ + data: 0, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + // Set state + expect(states[2]).toMatchObject({ + data: 0, + isFetching: true, + isSuccess: true, + isPreviousData: true, + }) + // New data + expect(states[3]).toMatchObject({ + data: 1, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + }) + + it('should keep the previous data on disabled query when keepPreviousData is set', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const [count, setCount] = createSignal(0) + + const state = createQuery( + () => [key(), count()], + async () => { + await sleep(10) + return count() + }, + { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const refetch = state.refetch + refetch() + + setActTimeout(() => { + setCount(1) + }, 20) + + setActTimeout(() => { + refetch() + }, 30) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(6) + + // Disabled query + expect(states[0]).toMatchObject({ + data: undefined, + isFetching: false, + isSuccess: false, + isPreviousData: false, + }) + // Fetching query + expect(states[1]).toMatchObject({ + data: undefined, + isFetching: true, + isSuccess: false, + isPreviousData: false, + }) + // Fetched query + expect(states[2]).toMatchObject({ + data: 0, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + // Set state + expect(states[3]).toMatchObject({ + data: 0, + isFetching: false, + isSuccess: true, + isPreviousData: true, + }) + // Fetching new query + expect(states[4]).toMatchObject({ + data: 0, + isFetching: true, + isSuccess: true, + isPreviousData: true, + }) + // Fetched new query + expect(states[5]).toMatchObject({ + data: 1, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + }) + + it('should keep the previous data on disabled query when keepPreviousData is set and switching query key multiple times', async () => { + const key = queryKey() + const states: Partial>[] = [] + + queryClient.setQueryData([key(), 10], 10) + + await sleep(10) + + function Page() { + const [count, setCount] = createSignal(10) + + const state = createQuery( + () => [key(), count()], + async () => { + await sleep(10) + return count() + }, + { enabled: false, keepPreviousData: true, notifyOnChangeProps: 'all' }, + ) + + createRenderEffect(() => { + const { data, isFetching, isSuccess, isPreviousData } = state + states.push({ data, isFetching, isSuccess, isPreviousData }) + }) + + createEffect(() => { + const refetch = state.refetch + setActTimeout(() => { + setCount(11) + }, 20) + setActTimeout(() => { + setCount(12) + }, 30) + setActTimeout(() => { + refetch() + }, 40) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(4) + + // Disabled query + expect(states[0]).toMatchObject({ + data: 10, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + // Set state + expect(states[1]).toMatchObject({ + data: 10, + isFetching: false, + isSuccess: true, + isPreviousData: true, + }) + // Refetch + expect(states[2]).toMatchObject({ + data: 10, + isFetching: true, + isSuccess: true, + isPreviousData: true, + }) + // Refetch done + expect(states[3]).toMatchObject({ + data: 12, + isFetching: false, + isSuccess: true, + isPreviousData: false, + }) + }) + + it('should use the correct query function when components use different configurations', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function FirstComponent() { + const state = createQuery( + key, + async () => { + await sleep(10) + return 1 + }, + { notifyOnChangeProps: 'all' }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + data: {state.data} +
+ ) + } + + function SecondComponent() { + createQuery(key, () => 2, { notifyOnChangeProps: 'all' }) + return null + } + + function Page() { + return ( + <> + + + + ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + + await waitFor(() => expect(states.length).toBe(4)) + + expect(states[0]).toMatchObject({ + data: undefined, + }) + expect(states[1]).toMatchObject({ + data: 1, + }) + expect(states[2]).toMatchObject({ + data: 1, + }) + // This state should be 1 instead of 2 + expect(states[3]).toMatchObject({ + data: 1, + }) + }) + + it('should be able to set different stale times for a query', async () => { + const key = queryKey() + const states1: CreateQueryResult[] = [] + const states2: CreateQueryResult[] = [] + + await queryClient.prefetchQuery(key(), async () => { + await sleep(10) + return 'prefetch' + }) + + await sleep(20) + + function FirstComponent() { + const state = createQuery( + key, + async () => { + await sleep(10) + return 'one' + }, + { + staleTime: 100, + }, + ) + createRenderEffect(() => { + states1.push({ ...state }) + }) + return null + } + + function SecondComponent() { + const state = createQuery( + key, + async () => { + await sleep(10) + return 'two' + }, + { + staleTime: 10, + }, + ) + createRenderEffect(() => { + states2.push({ ...state }) + }) + return null + } + + function Page() { + return ( + <> + + + + ) + } + + render(() => ( + + + + )) + + await sleep(200) + + expect(states1.length).toBe(4) + expect(states2.length).toBe(3) + + expect(states1).toMatchObject([ + // First render + { + data: 'prefetch', + isStale: false, + }, + // Second createQuery started fetching + { + data: 'prefetch', + isStale: false, + }, + // Second createQuery data came in + { + data: 'two', + isStale: false, + }, + // Data became stale after 100ms + { + data: 'two', + isStale: true, + }, + ]) + + expect(states2).toMatchObject([ + // First render, data is stale and starts fetching + { + data: 'prefetch', + isStale: true, + }, + // Second createQuery data came in + { + data: 'two', + isStale: false, + }, + // Data became stale after 5ms + { + data: 'two', + isStale: true, + }, + ]) + }) + + it('should re-render when a query becomes stale', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 'test', { + staleTime: 50, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(3) + expect(states[0]).toMatchObject({ isStale: true }) + expect(states[1]).toMatchObject({ isStale: false }) + expect(states[2]).toMatchObject({ isStale: true }) + }) + + it('should notify query cache when a query becomes stale', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const fn = jest.fn() + + const unsubscribe = queryCache.subscribe(fn) + + function Page() { + const state = createQuery(key, () => 'test', { + staleTime: 10, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(20) + unsubscribe() + + // 1. Query added -> loading + // 2. Observer result updated -> loading + // 3. Observer added + // 4. Query updated -> success + // 5. Observer result updated -> success + // 6. Query updated -> stale + // 7. Observer options updated + // 8. Observer result updated -> stale + // 9. Observer options updated + // Number 9 wont run in Solid JS + // Number 9 runs in react because the component re-renders after 8 + expect(fn).toHaveBeenCalledTimes(8) + }) + + it('should not re-render when it should only re-render on data changes and the data did not change', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(5) + return 'test' + }, + { + notifyOnChangeProps: ['data'], + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + const refetch = state.refetch + setActTimeout(() => { + refetch() + }, 10) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(30) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: undefined, + status: 'loading', + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'test', + status: 'success', + isFetching: false, + }) + }) + + // See https://github.com/tannerlinsley/react-query/issues/137 + it('should not override initial data in dependent queries', async () => { + const key1 = queryKey() + const key2 = queryKey() + + function Page() { + const first = createQuery(key1, () => 'data', { + enabled: false, + initialData: 'init', + }) + + const second = createQuery(key2, () => 'data', { + enabled: false, + initialData: 'init', + }) + + return ( +
+

First Data: {first.data}

+

Second Data: {second.data}

+
First Status: {first.status}
+
Second Status: {second.status}
+
+ ) + } + + render(() => ( + + + + )) + + screen.getByText('First Data: init') + screen.getByText('Second Data: init') + screen.getByText('First Status: success') + screen.getByText('Second Status: success') + }) + + it('should not override query configuration on render', async () => { + const key = queryKey() + + const queryFn1 = async () => { + await sleep(10) + return 'data1' + } + + const queryFn2 = async () => { + await sleep(10) + return 'data2' + } + + function Page() { + createQuery(key, queryFn1) + createQuery(key, queryFn2) + return null + } + + render(() => ( + + + + )) + + expect(queryCache.find(key())!.options.queryFn).toBe(queryFn1) + }) + + // TODO(lukemurray): this seems like a react implementation detail + it.skip('should render correct states even in case of useEffect triggering delays', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + // @ts-expect-error - we skip this test + const originalUseEffect = NotReact.useEffect + + // Try to simulate useEffect timing delay + // @ts-ignore + NotReact.useEffect = (...args: any[]) => { + originalUseEffect(() => { + setTimeout(() => { + args[0]() + }, 10) + }, args[1]) + } + + function Page() { + const state = createQuery(key, () => 'data', { staleTime: Infinity }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + queryClient.setQueryData(key(), 'data') + await sleep(50) + + // @ts-ignore + NotReact.useEffect = originalUseEffect + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ status: 'loading' }) + expect(states[1]).toMatchObject({ status: 'success' }) + }) + + it('should batch re-renders', async () => { + const key = queryKey() + + let renders = 0 + + const queryFn = async () => { + await sleep(15) + return 'data' + } + + function Page() { + createQuery(key, queryFn) + createQuery(key, queryFn) + renders++ + return null + } + + render(() => ( + + + + )) + + await sleep(20) + + // Since components are rendered once + // There wiil only be one pass + expect(renders).toBe(1) + }) + + it('should batch re-renders including hook callbacks', async () => { + const key = queryKey() + + let renders = 0 + let callbackCount = 0 + + const queryFn = async () => { + await sleep(10) + return 'data' + } + + function Page() { + const [count, setCount] = createSignal(0) + createQuery(key, queryFn, { + onSuccess: () => { + setCount((x) => x + 1) + }, + }) + createQuery(key, queryFn, { + onSuccess: () => { + setCount((x) => x + 1) + }, + }) + + createEffect(() => { + renders++ + callbackCount = count() + }) + + return
count: {count()}
+ } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('count: 2')) + + // Should be 3 instead of 5 + expect(renders).toBe(3) + // Both callbacks should have been executed + expect(callbackCount).toBe(2) + }) + + it('should render latest data even if react has discarded certain renders', async () => { + const key = queryKey() + + function Page() { + const [, setNewState] = createSignal('state') + const state = createQuery(key, () => 'data') + createEffect(() => { + setActTimeout(() => { + queryClient.setQueryData(key(), 'new') + // Update with same state to make react discard the next render + setNewState('state') + }, 10) + }) + return
{state.data}
+ } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('new')) + }) + + // See https://github.com/tannerlinsley/react-query/issues/170 + it('should start with status loading, fetchStatus idle if enabled is false', async () => { + const key1 = queryKey() + const key2 = queryKey() + + function Page() { + const first = createQuery(key1, () => 'data', { + enabled: false, + }) + const second = createQuery(key2, () => 'data') + + return ( +
+
+ First Status: {first.status}, {first.fetchStatus} +
+
+ Second Status: {second.status}, {second.fetchStatus} +
+
+ ) + } + + render(() => ( + + + + )) + + // use "act" to wait for state update and prevent console warning + + screen.getByText('First Status: loading, idle') + await waitFor(() => screen.getByText('Second Status: loading, fetching')) + await waitFor(() => screen.getByText('Second Status: success, idle')) + }) + + // See https://github.com/tannerlinsley/react-query/issues/144 + it('should be in "loading" state by default', async () => { + const key = queryKey() + + function Page() { + const { status } = createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + return
status: {status}
+ } + + render(() => ( + + + + )) + + screen.getByText('status: loading') + }) + + // See https://github.com/tannerlinsley/react-query/issues/147 + it('should not pass stringified variables to query function', async () => { + const key = queryKey() + const variables = { number: 5, boolean: false, object: {}, array: [] } + type CustomQueryKey = readonly [ReturnType, typeof variables] + const states: Partial>[] = [] + + // TODO(lukemurray): extract the query function to a variable queryFn + + function Page() { + const state = createQuery( + () => [key(), variables] as const, + async (ctx) => { + await sleep(10) + return ctx.queryKey + }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(20) + + expect(states[1]?.data).toEqual([key(), variables]) + }) + + it('should not refetch query on focus when `enabled` is set to `false`', async () => { + const key = queryKey() + const queryFn = jest.fn().mockReturnValue('data') + + function Page() { + const { data = 'default' } = createQuery(key, queryFn, { + enabled: false, + }) + + return ( +
+

{data}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('default')) + + window.dispatchEvent(new FocusEvent('focus')) + + expect(queryFn).not.toHaveBeenCalled() + }) + + it('should not refetch stale query on focus when `refetchOnWindowFocus` is set to `false`', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery(key, () => count++, { + staleTime: 0, + refetchOnWindowFocus: false, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + window.dispatchEvent(new FocusEvent('focus')) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) + expect(states[1]).toMatchObject({ data: 0, isFetching: false }) + }) + + it('should not refetch stale query on focus when `refetchOnWindowFocus` is set to a function that returns `false`', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery(key, () => count++, { + staleTime: 0, + refetchOnWindowFocus: () => false, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + window.dispatchEvent(new FocusEvent('focus')) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) + expect(states[1]).toMatchObject({ data: 0, isFetching: false }) + }) + + it('should not refetch fresh query on focus when `refetchOnWindowFocus` is set to `true`', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery(key, () => count++, { + staleTime: Infinity, + refetchOnWindowFocus: true, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + window.dispatchEvent(new FocusEvent('focus')) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) + expect(states[1]).toMatchObject({ data: 0, isFetching: false }) + }) + + it('should refetch fresh query on focus when `refetchOnWindowFocus` is set to `always`', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + return count++ + }, + { + staleTime: Infinity, + refetchOnWindowFocus: 'always', + }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(20) + + window.dispatchEvent(new FocusEvent('focus')) + + await sleep(20) + + await waitFor(() => expect(states.length).toBe(4)) + expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) + expect(states[1]).toMatchObject({ data: 0, isFetching: false }) + expect(states[2]).toMatchObject({ data: 0, isFetching: true }) + expect(states[3]).toMatchObject({ data: 1, isFetching: false }) + }) + + it('should calculate focus behaviour for refetchOnWindowFocus depending on function', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + return count++ + }, + { + staleTime: 0, + retry: 0, + refetchOnWindowFocus: (query) => (query.state.data || 0) < 1, + }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + return
data: {state.data}
+ } + + render(() => ( + + + + )) + + await screen.findByText('data: 0') + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ data: undefined, isFetching: true }) + expect(states[1]).toMatchObject({ data: 0, isFetching: false }) + + window.dispatchEvent(new FocusEvent('focus')) + + await screen.findByText('data: 1') + + // refetch should happen + expect(states.length).toBe(4) + + expect(states[2]).toMatchObject({ data: 0, isFetching: true }) + expect(states[3]).toMatchObject({ data: 1, isFetching: false }) + + await sleep(20) + + // no more refetch now + expect(states.length).toBe(4) + }) + + it('should refetch fresh query when refetchOnMount is set to always', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + await queryClient.prefetchQuery(key(), () => 'prefetched') + + function Page() { + const state = createQuery(key, () => 'data', { + refetchOnMount: 'always', + staleTime: Infinity, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: 'prefetched', + isStale: false, + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isStale: false, + isFetching: false, + }) + }) + + it('should refetch stale query when refetchOnMount is set to true', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + await queryClient.prefetchQuery(key(), () => 'prefetched') + + await sleep(10) + + function Page() { + const state = createQuery(key, () => 'data', { + refetchOnMount: true, + staleTime: 0, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: 'prefetched', + isStale: true, + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isStale: true, + isFetching: false, + }) + }) + + it('should set status to error if queryFn throws', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + () => { + return Promise.reject('Error test jaylen') + }, + { retry: false }, + ) + + return ( +
+

{state.status}

+

{state.error}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('error')) + await waitFor(() => screen.getByText('Error test jaylen')) + }) + + it('should throw error if queryFn throws and useErrorBoundary is in use', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + () => Promise.reject('Error test jaylen'), + { retry: false, useErrorBoundary: true }, + ) + + return ( +
+

{state.status}

+

{state.error}

+
+ ) + } + + render(() => ( + +
error boundary
}> + +
+
+ )) + + await waitFor(() => screen.getByText('error boundary')) + }) + + it('should update with data if we observe no properties and useErrorBoundary', async () => { + const key = queryKey() + + let result: CreateQueryResult | undefined + + function Page() { + const query = createQuery(key, () => Promise.resolve('data'), { + useErrorBoundary: true, + }) + + createEffect(() => { + result = query + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(result?.data).toBe('data') + }) + + it('should set status to error instead of throwing when error should not be thrown', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + () => Promise.reject('Local Error'), + { + retry: false, + useErrorBoundary: (err) => err !== 'Local Error', + }, + ) + + return ( +
+

{state.status}

+

{state.error}

+
+ ) + } + + render(() => ( + +
error boundary
}> + +
+
+ )) + + await waitFor(() => screen.getByText('error')) + await waitFor(() => screen.getByText('Local Error')) + }) + + it('should throw error instead of setting status when error should be thrown', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + () => Promise.reject(new Error('Remote Error')), + { + retry: false, + useErrorBoundary: (err) => err.message !== 'Local Error', + }, + ) + + return ( +
+

{state.status}

+

{state.error?.message ?? ''}

+
+ ) + } + + render(() => ( + + ( +
+
error boundary
+ {/* eslint-disable-next-line @typescript-eslint/no-unnecessary-condition */} +
{error?.message}
+
+ )} + > + +
+
+ )) + + await waitFor(() => screen.getByText('error boundary')) + await waitFor(() => screen.getByText('Remote Error')) + }) + + it('should continue retries when observers unmount and remount while waiting for a retry (#3031)', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const result = createQuery( + key, + async () => { + count++ + await sleep(10) + return Promise.reject('some error') + }, + { + retry: 2, + retryDelay: 100, + }, + ) + + return ( +
+
error: {result.error ?? 'null'}
+
failureCount: {result.failureCount}
+
+ ) + } + + function App() { + const [show, setShow] = createSignal(true) + + const toggle = () => setShow((s) => !s) + + return ( +
+ + {show() && } +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('failureCount: 1')) + fireEvent.click(screen.getByRole('button', { name: /hide/i })) + await waitFor(() => screen.getByRole('button', { name: /show/i })) + fireEvent.click(screen.getByRole('button', { name: /show/i })) + await waitFor(() => screen.getByText('error: some error')) + + expect(count).toBe(3) + }) + + it('should restart when observers unmount and remount while waiting for a retry when query was cancelled in between (#3031)', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const result = createQuery( + key, + async () => { + count++ + await sleep(10) + return Promise.reject('some error') + }, + { + retry: 2, + retryDelay: 100, + }, + ) + + return ( +
+
error: {result.error ?? 'null'}
+
failureCount: {result.failureCount}
+
+ ) + } + + function App() { + const [show, setShow] = createSignal(true) + + const toggle = () => setShow((s) => !s) + + return ( +
+ + + {show() && } +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('failureCount: 1')) + fireEvent.click(screen.getByRole('button', { name: /hide/i })) + fireEvent.click(screen.getByRole('button', { name: /cancel/i })) + await waitFor(() => screen.getByRole('button', { name: /show/i })) + fireEvent.click(screen.getByRole('button', { name: /show/i })) + await waitFor(() => screen.getByText('error: some error')) + + // initial fetch (1), which will be cancelled, followed by new mount(2) + 2 retries = 4 + expect(count).toBe(4) + }) + + it('should always fetch if refetchOnMount is set to always', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + await queryClient.prefetchQuery(key(), () => 'prefetched') + + function Page() { + const state = createQuery(key, () => 'data', { + refetchOnMount: 'always', + staleTime: 50, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return ( +
+
data: {state.data ?? 'null'}
+
isFetching: {state.isFetching}
+
isStale: {state.isStale}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: data')) + await waitFor(() => expect(states.length).toBe(3)) + + expect(states[0]).toMatchObject({ + data: 'prefetched', + isStale: false, + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isStale: false, + isFetching: false, + }) + expect(states[2]).toMatchObject({ + data: 'data', + isStale: true, + isFetching: false, + }) + }) + + it('should fetch if initial data is set', async () => { + const key = queryKey() + const states: DefinedCreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 'data', { + initialData: 'initial', + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(50) + + expect(states.length).toBe(2) + + expect(states[0]).toMatchObject({ + data: 'initial', + isStale: true, + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isStale: true, + isFetching: false, + }) + }) + + it('should not fetch if initial data is set with a stale time', async () => { + const key = queryKey() + const states: DefinedCreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 'data', { + staleTime: 50, + initialData: 'initial', + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: 'initial', + isStale: false, + isFetching: false, + }) + expect(states[1]).toMatchObject({ + data: 'initial', + isStale: true, + isFetching: false, + }) + }) + + it('should fetch if initial data updated at is older than stale time', async () => { + const key = queryKey() + const states: DefinedCreateQueryResult[] = [] + + const oneSecondAgo = Date.now() - 1000 + + function Page() { + const state = createQuery(key, () => 'data', { + staleTime: 50, + initialData: 'initial', + initialDataUpdatedAt: oneSecondAgo, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(3) + expect(states[0]).toMatchObject({ + data: 'initial', + isStale: true, + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isStale: false, + isFetching: false, + }) + expect(states[2]).toMatchObject({ + data: 'data', + isStale: true, + isFetching: false, + }) + }) + + it('should fetch if "initial data updated at" is exactly 0', async () => { + const key = queryKey() + const states: DefinedCreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 'data', { + staleTime: 10 * 1000, // 10 seconds + initialData: 'initial', + initialDataUpdatedAt: 0, + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + expect(states[0]).toMatchObject({ + data: 'initial', + isStale: true, + isFetching: true, + }) + expect(states[1]).toMatchObject({ + data: 'data', + isStale: false, + isFetching: false, + }) + }) + + it('should keep initial data when the query key changes', async () => { + const key = queryKey() + const states: DefinedCreateQueryResult<{ count: number }>[] = [] + + function Page() { + const [count, setCount] = createSignal(0) + const state = createQuery( + () => [key(), count()], + () => ({ count: 10 }), + { + staleTime: Infinity, + initialData: () => ({ count: count() }), + }, + ) + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect(() => { + setActTimeout(() => { + setCount(1) + }, 10) + }, []) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + + expect(states.length).toBe(2) + // Initial + expect(states[0]).toMatchObject({ data: { count: 0 } }) + // Set state + expect(states[1]).toMatchObject({ data: { count: 1 } }) + }) + + it('should retry specified number of times', async () => { + const key = queryKey() + + const queryFn = jest.fn() + queryFn.mockImplementation(() => { + return Promise.reject('Error test Barrett') + }) + + function Page() { + const state = createQuery(key, queryFn, { + retry: 1, + retryDelay: 1, + }) + + return ( +
+

{state.status}

+

Failed {state.failureCount} times

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('loading')) + await waitFor(() => screen.getByText('error')) + + // query should fail `retry + 1` times, since first time isn't a "retry" + await waitFor(() => screen.getByText('Failed 2 times')) + + expect(queryFn).toHaveBeenCalledTimes(2) + }) + + it('should not retry if retry function `false`', async () => { + const key = queryKey() + + const queryFn = jest.fn() + + queryFn.mockImplementationOnce(() => { + return Promise.reject('Error test Tanner') + }) + + queryFn.mockImplementation(() => { + return Promise.reject('NoRetry') + }) + + function Page() { + const state = createQuery(key, queryFn, { + retryDelay: 1, + retry: (_failureCount, err) => err !== 'NoRetry', + }) + + return ( +
+

{state.status}

+

Failed {state.failureCount} times

+

{state.error}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('loading')) + await waitFor(() => screen.getByText('error')) + + await waitFor(() => screen.getByText('Failed 2 times')) + await waitFor(() => screen.getByText('NoRetry')) + + expect(queryFn).toHaveBeenCalledTimes(2) + }) + + it('should extract retryDelay from error', async () => { + const key = queryKey() + + type DelayError = { delay: number } + + const queryFn = jest.fn() + queryFn.mockImplementation(() => { + return Promise.reject({ delay: 50 }) + }) + + function Page() { + const state = createQuery(key, queryFn, { + retry: 1, + retryDelay: (_, error: DelayError) => error.delay, + }) + + return ( +
+

{state.status}

+

Failed {state.failureCount} times

+
+ ) + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(queryFn).toHaveBeenCalledTimes(1) + + await waitFor(() => screen.getByText('Failed 2 times')) + + expect(queryFn).toHaveBeenCalledTimes(2) + }) + + // See https://github.com/tannerlinsley/react-query/issues/160 + it('should continue retry after focus regain', async () => { + const key = queryKey() + + // make page unfocused + const visibilityMock = mockVisibilityState('hidden') + + let count = 0 + + function Page() { + const query = createQuery( + key, + () => { + count++ + return Promise.reject(`fetching error ${count}`) + }, + { + retry: 3, + retryDelay: 1, + }, + ) + + return ( +
+
error {String(query.error)}
+
status {query.status}
+
failureCount {query.failureCount}
+
+ ) + } + + render(() => ( + + + + )) + + // The query should display the first error result + await waitFor(() => screen.getByText('failureCount 1')) + await waitFor(() => screen.getByText('status loading')) + await waitFor(() => screen.getByText('error null')) + + // Check if the query really paused + await sleep(10) + await waitFor(() => screen.getByText('failureCount 1')) + + visibilityMock.mockRestore() + window.dispatchEvent(new FocusEvent('focus')) + + // Wait for the final result + await waitFor(() => screen.getByText('failureCount 4')) + await waitFor(() => screen.getByText('status error')) + await waitFor(() => screen.getByText('error fetching error 4')) + + // Check if the query really stopped + await sleep(10) + await waitFor(() => screen.getByText('failureCount 4')) + + // Check if the error has been logged in the console + expect(mockLogger.error).toHaveBeenCalledWith('fetching error 4') + }) + + it('should fetch on mount when a query was already created with setQueryData', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + queryClient.setQueryData(key(), 'prefetched') + + function Page() { + const state = createQuery(key, () => 'data') + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(states.length).toBe(2) + expect(states).toMatchObject([ + { + data: 'prefetched', + isFetching: true, + isStale: true, + }, + { + data: 'data', + isFetching: false, + isStale: true, + }, + ]) + }) + + it('should refetch after focus regain', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + // make page unfocused + const visibilityMock = mockVisibilityState('hidden') + + // set data in cache to check if the hook query fn is actually called + queryClient.setQueryData(key(), 'prefetched') + + function Page() { + const state = createQuery(key, async () => { + await sleep(10) + return 'data' + }) + createRenderEffect(() => { + states.push({ ...state }) + }) + return ( +
+ {state.data}, {state.isStale}, {state.isFetching} +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => expect(states.length).toBe(2)) + + // reset visibilityState to original value + visibilityMock.mockRestore() + window.dispatchEvent(new FocusEvent('focus')) + + await waitFor(() => expect(states.length).toBe(4)) + + expect(states).toMatchObject([ + { + data: 'prefetched', + isFetching: true, + isStale: true, + }, + { + data: 'data', + isFetching: false, + isStale: true, + }, + { + data: 'data', + isFetching: true, + isStale: true, + }, + { + data: 'data', + isFetching: false, + isStale: true, + }, + ]) + }) + + // See https://github.com/tannerlinsley/react-query/issues/195 + it('should refetch if stale after a prefetch', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + const queryFn = jest.fn() + queryFn.mockImplementation(() => 'data') + + const prefetchQueryFn = jest.fn() + prefetchQueryFn.mockImplementation(() => 'not yet...') + + await queryClient.prefetchQuery(key(), prefetchQueryFn, { + staleTime: 10, + }) + + await sleep(11) + + function Page() { + const state = createQuery(key, queryFn) + createRenderEffect(() => { + states.push({ ...state }) + }) + return null + } + + render(() => ( + + + + )) + + await waitFor(() => expect(states.length).toBe(2)) + + expect(prefetchQueryFn).toHaveBeenCalledTimes(1) + expect(queryFn).toHaveBeenCalledTimes(1) + }) + + it('should not refetch if not stale after a prefetch', async () => { + const key = queryKey() + + const queryFn = jest.fn() + queryFn.mockImplementation(() => 'data') + + const prefetchQueryFn = jest.fn, unknown[]>() + prefetchQueryFn.mockImplementation(async () => { + await sleep(10) + return 'not yet...' + }) + + await queryClient.prefetchQuery(key(), prefetchQueryFn, { + staleTime: 1000, + }) + + await sleep(0) + + function Page() { + createQuery(key, queryFn, { + staleTime: 1000, + }) + return null + } + + render(() => ( + + + + )) + + await sleep(0) + + expect(prefetchQueryFn).toHaveBeenCalledTimes(1) + expect(queryFn).toHaveBeenCalledTimes(0) + }) + + // See https://github.com/tannerlinsley/react-query/issues/190 + it('should reset failureCount on successful fetch', async () => { + const key = queryKey() + + function Page() { + let counter = 0 + + const query = createQuery( + key, + async () => { + if (counter < 2) { + counter++ + throw new Error('error') + } else { + return 'data' + } + }, + { retryDelay: 10 }, + ) + + return ( +
+
failureCount {query.failureCount}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('failureCount 2')) + await waitFor(() => screen.getByText('failureCount 0')) + }) + + // See https://github.com/tannerlinsley/react-query/issues/199 + it('should use prefetched data for dependent query', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const [enabled, setEnabled] = createSignal(false) + const [isPrefetched, setPrefetched] = createSignal(false) + + const query = createQuery( + key, + async () => { + count++ + await sleep(10) + return count + }, + { + get enabled() { + return enabled() + }, + }, + ) + + createEffect(() => { + async function prefetch() { + await queryClient.prefetchQuery(key(), () => + Promise.resolve('prefetched data'), + ) + setPrefetched(true) + } + prefetch() + }) + + return ( +
+ {isPrefetched() &&
isPrefetched
} + +
data: {query.data}
+
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('isPrefetched')) + + fireEvent.click(screen.getByText('setKey')) + await waitFor(() => screen.getByText('data: prefetched data')) + await waitFor(() => screen.getByText('data: 1')) + expect(count).toBe(1) + }) + + it('should support dependent queries via the enable config option', async () => { + const key = queryKey() + + function Page() { + const [shouldFetch, setShouldFetch] = createSignal(false) + + const query = createQuery(key, () => 'data', { + get enabled() { + return shouldFetch() + }, + }) + + return ( +
+
FetchStatus: {query.fetchStatus}
+

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

+ {query.isStale ? ( + + ) : null} +
+ ) + } + + render(() => ( + + + + )) + + screen.getByText('FetchStatus: idle') + screen.getByText('Data: no data') + + fireEvent.click(screen.getByText('fetch')) + + await waitFor(() => screen.getByText('FetchStatus: fetching')) + await waitFor(() => [ + screen.getByText('FetchStatus: idle'), + screen.getByText('Data: data'), + ]) + }) + + it('should mark query as fetching, when using initialData', async () => { + const key = queryKey() + const results: DefinedCreateQueryResult[] = [] + + function Page() { + const result = createQuery(key, () => 'serverData', { + initialData: 'data', + }) + + createRenderEffect(() => { + results.push({ ...result }) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(results.length).toBe(2) + expect(results[0]).toMatchObject({ data: 'data', isFetching: true }) + expect(results[1]).toMatchObject({ data: 'serverData', isFetching: false }) + }) + + it('should initialize state properly, when initialData is falsy', async () => { + const key = queryKey() + const results: DefinedCreateQueryResult[] = [] + + function Page() { + const result = createQuery(key, () => 1, { initialData: 0 }) + + createRenderEffect(() => { + results.push({ ...result }) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(results.length).toBe(2) + expect(results[0]).toMatchObject({ data: 0, isFetching: true }) + expect(results[1]).toMatchObject({ data: 1, isFetching: false }) + }) + + // // See https://github.com/tannerlinsley/react-query/issues/214 + it('data should persist when enabled is changed to false', async () => { + const key = queryKey() + const results: DefinedCreateQueryResult[] = [] + + function Page() { + const [shouldFetch, setShouldFetch] = createSignal(true) + + const result = createQuery(key, () => 'fetched data', { + get enabled() { + return shouldFetch() + }, + get initialData() { + return shouldFetch() ? 'initial' : 'initial falsy' + }, + }) + + createRenderEffect(() => { + results.push({ ...result }) + }) + + createEffect(() => { + setActTimeout(() => { + setShouldFetch(false) + }, 5) + }) + + return null + } + + render(() => ( + + + + )) + + await sleep(50) + expect(results.length).toBe(2) + expect(results[0]).toMatchObject({ data: 'initial', isStale: true }) + expect(results[1]).toMatchObject({ data: 'fetched data', isStale: true }) + // Wont render 3rd time, because data is still the same + }) + + it('it should support enabled:false in query object syntax', async () => { + const key = queryKey() + const queryFn = jest.fn() + queryFn.mockImplementation(() => 'data') + + function Page() { + const { fetchStatus } = createQuery({ + queryKey: key, + queryFn, + enabled: false, + }) + return
fetchStatus: {fetchStatus}
+ } + + render(() => ( + + + + )) + + expect(queryFn).not.toHaveBeenCalled() + expect(queryCache.find(key())).not.toBeUndefined() + screen.getByText('fetchStatus: idle') + }) + + // See https://github.com/tannerlinsley/react-query/issues/360 + it('should init to status:loading, fetchStatus:idle when enabled is false', async () => { + const key = queryKey() + + function Page() { + const query = createQuery(key, () => 'data', { + enabled: false, + }) + + return ( +
+
+ status: {query.status}, {query.fetchStatus} +
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('status: loading, idle')) + }) + + it('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => { + const key = queryKey() + + function Page() { + const query = createQuery(key, () => 'fetched data', { + cacheTime: Infinity, + }) + return
{query.data}
+ } + + const result = render(() => ( + + + + )) + + await waitFor(() => screen.getByText('fetched data')) + + result.unmount() + + const query = queryCache.find(key()) + // @ts-expect-error + expect(query!.cacheTimeout).toBe(undefined) + }) + + it('should not cause memo churn when data does not change', async () => { + const key = queryKey() + const queryFn = jest.fn().mockReturnValue('data') + const memoFn = jest.fn() + + function Page() { + const result = createQuery(key, async () => { + await sleep(10) + return ( + queryFn() || { + data: { + nested: true, + }, + } + ) + }) + + createMemo(() => { + memoFn() + return result.data + }) + + return ( +
+
status {result.status}
+
isFetching {result.isFetching ? 'true' : 'false'}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('status loading')) + await waitFor(() => screen.getByText('status success')) + fireEvent.click(screen.getByText('refetch')) + await waitFor(() => screen.getByText('isFetching true')) + await waitFor(() => screen.getByText('isFetching false')) + expect(queryFn).toHaveBeenCalledTimes(2) + expect(memoFn).toHaveBeenCalledTimes(2) + }) + + it('should update data upon interval changes', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const [int, setInt] = createSignal(200) + const state = createQuery(key, () => count++, { + get refetchInterval() { + return int() + }, + }) + + createEffect(() => { + if (state.data === 2) { + setInt(0) + } + }) + + return
count: {state.data}
+ } + + render(() => ( + + + + )) + + // mount + await waitFor(() => screen.getByText('count: 0')) + await waitFor(() => screen.getByText('count: 1')) + await waitFor(() => screen.getByText('count: 2')) + }) + + it('should refetch in an interval depending on function result', async () => { + const key = queryKey() + let count = 0 + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + return count++ + }, + { + refetchInterval: (data = 0) => (data < 2 ? 10 : false), + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+

count: {state.data}

+

status: {state.status}

+

data: {state.data}

+

refetch: {state.isRefetching}

+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('count: 2')) + + expect(states.length).toEqual(6) + + expect(states).toMatchObject([ + { + status: 'loading', + isFetching: true, + data: undefined, + }, + { + status: 'success', + isFetching: false, + data: 0, + }, + { + status: 'success', + isFetching: true, + data: 0, + }, + { + status: 'success', + isFetching: false, + data: 1, + }, + { + status: 'success', + isFetching: true, + data: 1, + }, + { + status: 'success', + isFetching: false, + data: 2, + }, + ]) + }) + + it('should not interval fetch with a refetchInterval of 0', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key, () => 1, { + refetchInterval: 0, + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return
count: {state.data}
+ } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('count: 1')) + + await sleep(10) //extra sleep to make sure we're not re-fetching + + expect(states.length).toEqual(2) + + expect(states).toMatchObject([ + { + status: 'loading', + isFetching: true, + data: undefined, + }, + { + status: 'success', + isFetching: false, + data: 1, + }, + ]) + }) + + it('should accept an empty string as query key', async () => { + function Page() { + const result = createQuery( + () => [''], + (ctx) => ctx.queryKey, + ) + return <>{JSON.stringify(result.data)} + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('')) + }) + + it('should accept an object as query key', async () => { + function Page() { + const result = createQuery( + () => [{ a: 'a' }], + (ctx) => ctx.queryKey, + ) + return <>{JSON.stringify(result.data)} + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('[{"a":"a"}]')) + }) + + it('should refetch if any query instance becomes enabled', async () => { + const key = queryKey() + + const queryFn = jest.fn().mockReturnValue('data') + + function Disabled() { + createQuery(key, queryFn, { enabled: false }) + return null + } + + function Page() { + const [enabled, setEnabled] = createSignal(false) + const result = createQuery(key, queryFn, { + get enabled() { + return enabled() + }, + }) + return ( + <> + +
{result.data}
+ + + ) + } + + render(() => ( + + + + )) + expect(queryFn).toHaveBeenCalledTimes(0) + fireEvent.click(screen.getByText('enable')) + await waitFor(() => screen.getByText('data')) + expect(queryFn).toHaveBeenCalledTimes(1) + }) + + it('should use placeholder data while the query loads', async () => { + const key1 = queryKey() + + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key1, () => 'data', { + placeholderData: 'placeholder', + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+

Data: {state.data}

+
Status: {state.status}
+
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: data')) + + expect(states).toMatchObject([ + { + isSuccess: true, + isPlaceholderData: true, + data: 'placeholder', + }, + { + isSuccess: true, + isPlaceholderData: false, + data: 'data', + }, + ]) + }) + + it('should use placeholder data even for disabled queries', async () => { + const key1 = queryKey() + + const states: { state: CreateQueryResult; count: number }[] = [] + + function Page() { + const [count, setCount] = createSignal(0) + + const state = createQuery(key1, () => 'data', { + placeholderData: 'placeholder', + get enabled() { + return count() === 0 + }, + }) + + createRenderEffect(() => { + states.push({ state: { ...state }, count: count() }) + }) + + createEffect(() => { + setCount(1) + }) + + return ( +
+

Data: {state.data}

+
Status: {state.status}
+
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: data')) + + expect(states).toMatchObject([ + { + state: { + isSuccess: true, + isPlaceholderData: true, + data: 'placeholder', + }, + count: 0, + }, + { + state: { + isSuccess: true, + isPlaceholderData: true, + data: 'placeholder', + }, + count: 1, + }, + { + state: { + isSuccess: true, + isPlaceholderData: false, + data: 'data', + }, + count: 1, + }, + ]) + }) + + it('placeholder data should run through select', async () => { + const key1 = queryKey() + + const states: CreateQueryResult[] = [] + + function Page() { + const state = createQuery(key1, () => 1, { + placeholderData: 23, + select: (data) => String(data * 2), + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+

Data: {state.data}

+
Status: {state.status}
+
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: 2')) + + expect(states).toMatchObject([ + { + isSuccess: true, + isPlaceholderData: true, + data: '46', + }, + { + isSuccess: true, + isPlaceholderData: false, + data: '2', + }, + ]) + }) + + it('placeholder data function result should run through select', async () => { + const key1 = queryKey() + + const states: CreateQueryResult[] = [] + let placeholderFunctionRunCount = 0 + + function Page() { + const state = createQuery(key1, () => 1, { + placeholderData: () => { + placeholderFunctionRunCount++ + return 23 + }, + select: (data) => String(data * 2), + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+

Data: {state.data}

+
Status: {state.status}
+
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: 2')) + + expect(states).toMatchObject([ + { + isSuccess: true, + isPlaceholderData: true, + data: '46', + }, + { + isSuccess: true, + isPlaceholderData: false, + data: '2', + }, + ]) + + expect(placeholderFunctionRunCount).toEqual(1) + }) + + // React Specific implementation. Not really needed since solid functions are stable + it.skip('select should only run when dependencies change if memoized', async () => { + const key1 = queryKey() + + let selectRun = 0 + + function Page() { + //@ts-expect-error skip this test + const [count, inc] = NotReact.useReducer((prev) => prev + 1, 2) + + const state = createQuery( + key1, + async () => { + await sleep(10) + return 0 + }, + { + //@ts-expect-error skip this test + select: NotReact.useCallback( + (data: number) => { + selectRun++ + return `selected ${data + count}` + }, + [count], + ), + placeholderData: 99, + }, + ) + + return ( +
+

Data: {state.data}

+ +
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: selected 101')) // 99 + 2 + expect(selectRun).toBe(1) + + await waitFor(() => screen.getByText('Data: selected 2')) // 0 + 2 + expect(selectRun).toBe(2) + + fireEvent.click(screen.getByRole('button', { name: /inc/i })) + + await waitFor(() => screen.getByText('Data: selected 3')) // 0 + 3 + expect(selectRun).toBe(3) + }) + + it('select should always return the correct state', async () => { + const key1 = queryKey() + + function Page() { + const [count, setCount] = createSignal(2) + const [forceValue, setForceValue] = createSignal(1) + + const inc = () => { + setCount((prev) => prev + 1) + } + + const forceUpdate = () => { + setForceValue((prev) => prev + 1) + } + + const state = createQuery( + key1, + async () => { + await sleep(10) + return 0 + }, + { + get select() { + const currentCount = count() + return (data: number) => `selected ${data + currentCount}` + }, + placeholderData: 99, + }, + ) + + return ( +
+

Data: {state.data}

+

forceValue: {forceValue()}

+ + +
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: selected 101')) // 99 + 2 + + await waitFor(() => screen.getByText('Data: selected 2')) // 0 + 2 + + fireEvent.click(screen.getByRole('button', { name: /inc/i })) + + await waitFor(() => screen.getByText('Data: selected 3')) // 0 + 3 + + fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i })) + + await waitFor(() => screen.getByText('forceValue: 2')) + // data should still be 3 after an independent re-render + await waitFor(() => screen.getByText('Data: selected 3')) + }) + + it('select should structurally share data', async () => { + const key1 = queryKey() + const states: Array> = [] + + function Page() { + const [forceValue, setForceValue] = createSignal(1) + + const state = createQuery( + key1, + async () => { + await sleep(10) + return [1, 2] + }, + { + select: (res) => res.map((x) => x + 1), + }, + ) + + createEffect(() => { + if (state.data) { + states.push(state.data) + } + }) + + const forceUpdate = () => { + setForceValue((prev) => prev + 1) + } + + return ( +
+

Data: {JSON.stringify(state.data)}

+

forceValue: {forceValue}

+ +
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => screen.getByText('Data: [2,3]')) + expect(states).toHaveLength(1) + + fireEvent.click(screen.getByRole('button', { name: /forceUpdate/i })) + + await waitFor(() => screen.getByText('forceValue: 2')) + await waitFor(() => screen.getByText('Data: [2,3]')) + + // effect should not be triggered again due to structural sharing + expect(states).toHaveLength(1) + }) + + it('should cancel the query function when there are no more subscriptions', async () => { + const key = queryKey() + let cancelFn: jest.Mock = jest.fn() + + const queryFn = ({ signal }: { signal?: AbortSignal }) => { + const promise = new Promise((resolve, reject) => { + cancelFn = jest.fn(() => reject('Cancelled')) + signal?.addEventListener('abort', cancelFn) + sleep(10).then(() => resolve('OK')) + }) + + return promise + } + + function Page() { + const state = createQuery(key, queryFn) + return ( +
+

Status: {state.status}

+
+ ) + } + + render(() => ( + + + + + + )) + + await waitFor(() => screen.getByText('off')) + + if (typeof AbortSignal === 'function') { + expect(cancelFn).toHaveBeenCalled() + } + }) + + it('should cancel the query if the signal was consumed and there are no more subscriptions', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + const queryFn: QueryFunction< + string, + readonly [ReturnType, number] + > = async (ctx) => { + const [, limit] = ctx.queryKey + const value = limit % 2 && ctx.signal ? 'abort' : `data ${limit}` + await sleep(25) + return value + } + + function Page(props: { limit: number }) { + const state = createQuery(() => [key(), props.limit] as const, queryFn) + states[props.limit] = state + return ( +
+

Status: {state.status}

+

data: {state.data}

+
+ ) + } + + render(() => ( + + + + + + + + + )) + + await waitFor(() => screen.getByText('off')) + await sleep(20) + + await waitFor(() => expect(states).toHaveLength(4)) + + expect(queryCache.find([key(), 0])?.state).toMatchObject({ + data: 'data 0', + status: 'success', + dataUpdateCount: 1, + }) + + if (typeof AbortSignal === 'function') { + expect(queryCache.find([key(), 1])?.state).toMatchObject({ + data: undefined, + status: 'loading', + fetchStatus: 'idle', + }) + } else { + expect(queryCache.find([key(), 1])?.state).toMatchObject({ + data: 'data 1', + status: 'success', + dataUpdateCount: 1, + }) + } + + expect(queryCache.find([key(), 2])?.state).toMatchObject({ + data: 'data 2', + status: 'success', + dataUpdateCount: 1, + }) + + if (typeof AbortSignal === 'function') { + expect(queryCache.find([key(), 3])?.state).toMatchObject({ + data: undefined, + status: 'loading', + fetchStatus: 'idle', + }) + } else { + expect(queryCache.find([key(), 3])?.state).toMatchObject({ + data: 'data 3', + status: 'success', + dataUpdateCount: 1, + }) + } + }) + + it('should refetch when quickly switching to a failed query', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + const queryFn = async () => { + await sleep(50) + return 'OK' + } + + function Page() { + const [id, setId] = createSignal(1) + const [hasChanged, setHasChanged] = createSignal(false) + + const state = createQuery(() => [key(), id()], queryFn) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createEffect( + on(hasChanged, () => { + setId((prevId) => (prevId === 1 ? 2 : 1)) + setHasChanged(true) + }), + ) + + return null + } + + render(() => ( + + + + )) + + await sleep(100) + expect(states.length).toBe(2) + // Load query 1 + expect(states[0]).toMatchObject({ + status: 'loading', + error: null, + }) + // No rerenders - No state updates + // Loaded query 1 + expect(states[1]).toMatchObject({ + status: 'success', + error: null, + }) + }) + + it('should update query state and refetch when reset with resetQueries', async () => { + const key = queryKey() + const states: Optional>>[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + count++ + return count + }, + { staleTime: Infinity }, + ) + + createRenderEffect(() => { + const { data, isLoading, isFetching, isSuccess, isStale } = state + + states.push({ + data, + isLoading, + isFetching, + isSuccess, + isStale, + }) + }) + + return ( +
+ +
data: {state.data ?? 'null'}
+
isFetching: {state.isFetching}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByRole('button', { name: /reset/i })) + + await waitFor(() => expect(states.length).toBe(4)) + + await waitFor(() => screen.getByText('data: 2')) + + expect(count).toBe(2) + + expect(states[0]).toMatchObject({ + data: undefined, + isLoading: true, + isFetching: true, + isSuccess: false, + isStale: true, + }) + expect(states[1]).toMatchObject({ + data: 1, + isLoading: false, + isFetching: false, + isSuccess: true, + isStale: false, + }) + expect(states[2]).toMatchObject({ + data: undefined, + isLoading: true, + isFetching: true, + isSuccess: false, + isStale: true, + }) + expect(states[3]).toMatchObject({ + data: 2, + isLoading: false, + isFetching: false, + isSuccess: true, + isStale: false, + }) + }) + + it('should update query state and not refetch when resetting a disabled query with resetQueries', async () => { + const key = queryKey() + const states: Partial>[] = [] + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + count++ + return count + }, + { staleTime: Infinity, enabled: false, notifyOnChangeProps: 'all' }, + ) + + createRenderEffect(() => { + const { data, isLoading, isFetching, isSuccess, isStale } = state + states.push({ + data, + isLoading, + isFetching, + isSuccess, + isStale, + }) + }) + + const { refetch } = state + + return ( +
+ + +
data: {state.data ?? 'null'}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: null')) + fireEvent.click(screen.getByRole('button', { name: /refetch/i })) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByRole('button', { name: /reset/i })) + + await waitFor(() => screen.getByText('data: null')) + await waitFor(() => expect(states.length).toBe(4)) + + expect(count).toBe(1) + + expect(states[0]).toMatchObject({ + data: undefined, + isLoading: true, + isFetching: false, + isSuccess: false, + isStale: true, + }) + expect(states[1]).toMatchObject({ + data: undefined, + isLoading: true, + isFetching: true, + isSuccess: false, + isStale: true, + }) + expect(states[2]).toMatchObject({ + data: 1, + isLoading: false, + isFetching: false, + isSuccess: true, + isStale: false, + }) + expect(states[3]).toMatchObject({ + data: undefined, + isLoading: true, + isFetching: false, + isSuccess: false, + isStale: true, + }) + }) + + it('should only call the query hash function once each render', async () => { + const key = queryKey() + + let hashes = 0 + let renders = 0 + + function queryKeyHashFn(x: any) { + hashes++ + return JSON.stringify(x) + } + + function Page() { + const state = createQuery(key, () => 'test', { queryKeyHashFn }) + createEffect( + on( + () => state.status, + () => { + renders++ + }, + ), + ) + return null + } + + render(() => ( + + + + )) + + await sleep(10) + + expect(renders).toBe(2) + expect(hashes).toBe(2) + }) + + it('should refetch when changed enabled to true in error state', async () => { + const queryFn = jest.fn() + queryFn.mockImplementation(async () => { + await sleep(10) + return Promise.reject(new Error('Suspense Error Bingo')) + }) + + function Page(props: { enabled: boolean }) { + const state = createQuery(() => ['key'], queryFn, { + get enabled() { + return props.enabled + }, + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }) + + return ( + rendered}> + +
status: loading
+
+ +
error
+
+
+ ) + } + + function App() { + const [enabled, setEnabled] = createSignal(true) + const toggle = () => setEnabled((prev) => !prev) + + return ( +
+ + +
+ ) + } + + render(() => ( + + + + )) + + // initial state check + screen.getByText('status: loading') + + // // render error state component + await waitFor(() => screen.getByText('error')) + expect(queryFn).toBeCalledTimes(1) + + // change to enabled to false + fireEvent.click(screen.getByLabelText('retry')) + await waitFor(() => screen.getByText('error')) + expect(queryFn).toBeCalledTimes(1) + + // // change to enabled to true + fireEvent.click(screen.getByLabelText('retry')) + expect(queryFn).toBeCalledTimes(2) + }) + + it('should refetch when query key changed when previous status is error', async () => { + function Page(props: { id: number }) { + const state = createQuery( + () => [props.id], + async () => { + await sleep(10) + if (props.id % 2 === 1) { + return Promise.reject(new Error('Error')) + } else { + return 'data' + } + }, + { + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }, + ) + + return ( + rendered}> + +
status: loading
+
+ +
error
+
+
+ ) + } + + function App() { + const [id, setId] = createSignal(1) + const changeId = () => setId((x) => x + 1) + + return ( +
+ + +
+ ) + } + + render(() => ( + + + + )) + + // initial state check + screen.getByText('status: loading') + + // render error state component + await waitFor(() => screen.getByText('error')) + + // change to unmount query + fireEvent.click(screen.getByLabelText('change')) + await waitFor(() => screen.getByText('rendered')) + + // change to mount new query + fireEvent.click(screen.getByLabelText('change')) + await waitFor(() => screen.getByText('error')) + }) + + it('should refetch when query key changed when switching between erroneous queries', async () => { + function Page(props: { id: boolean }) { + const state = createQuery( + () => [props.id], + async () => { + await sleep(10) + return Promise.reject(new Error('Error')) + }, + { + retry: false, + retryOnMount: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + }, + ) + return ( + rendered}> + +
status: fetching
+
+ +
error
+
+
+ ) + } + + function App() { + const [value, setValue] = createSignal(true) + const toggle = () => setValue((x) => !x) + + return ( +
+ + +
+ ) + } + + render(() => ( + + + + )) + + // initial state check + screen.getByText('status: fetching') + + // render error state component + await waitFor(() => screen.getByText('error')) + + // change to mount second query + fireEvent.click(screen.getByLabelText('change')) + await waitFor(() => screen.getByText('status: fetching')) + await waitFor(() => screen.getByText('error')) + + // change to mount first query again + fireEvent.click(screen.getByLabelText('change')) + await waitFor(() => screen.getByText('status: fetching')) + await waitFor(() => screen.getByText('error')) + }) + + it('should have no error in loading state when refetching after error occurred', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const error = new Error('oops') + + let count = 0 + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + if (count === 0) { + count++ + throw error + } + return 5 + }, + { + retry: false, + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( + data: {state.data}}> + +
status: loading
+
+ +
+
error
+ +
+
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('error')) + + fireEvent.click(screen.getByRole('button', { name: 'refetch' })) + await waitFor(() => screen.getByText('data: 5')) + + await waitFor(() => expect(states.length).toBe(4)) + + expect(states[0]).toMatchObject({ + status: 'loading', + data: undefined, + error: null, + }) + + expect(states[1]).toMatchObject({ + status: 'error', + data: undefined, + error, + }) + + expect(states[2]).toMatchObject({ + status: 'loading', + data: undefined, + error: null, + }) + + expect(states[3]).toMatchObject({ + status: 'success', + data: 5, + error: null, + }) + }) + + describe('networkMode online', () => { + it('online queries should not start fetching if you are offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + const states: Array = [] + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'data' + }, + }) + + createEffect(() => { + states.push(state.fetchStatus) + }) + + return ( +
+
+ status: {state.status}, isPaused: {String(state.isPaused)} +
+
data: {state.data}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('status: loading, isPaused: true')) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => screen.getByText('status: success, isPaused: false')) + await waitFor(() => { + expect(screen.getByText('data: data')).toBeInTheDocument() + }) + + expect(states).toEqual(['paused', 'fetching', 'idle']) + + onlineMock.mockRestore() + }) + + it('online queries should not refetch if you are offline', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus}, + failureCount: {state.failureCount} +
+
data: {state.data}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: data1')) + + const onlineMock = mockNavigatorOnLine(false) + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + + await waitFor(() => + screen.getByText( + 'status: success, fetchStatus: paused, failureCount: 0', + ), + ) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + screen.getByText( + 'status: success, fetchStatus: fetching, failureCount: 0', + ), + ) + await waitFor(() => + screen.getByText('status: success, fetchStatus: idle, failureCount: 0'), + ) + + await waitFor(() => { + expect(screen.getByText('data: data2')).toBeInTheDocument() + }) + + onlineMock.mockRestore() + }) + + it('online queries should not refetch if you are offline and refocus', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: data1')) + + const onlineMock = mockNavigatorOnLine(false) + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: paused'), + ) + + window.dispatchEvent(new FocusEvent('focus')) + await sleep(15) + + await waitFor(() => + expect(screen.queryByText('data: data2')).not.toBeInTheDocument(), + ) + expect(count).toBe(1) + onlineMock.mockRestore() + }) + + it('online queries should not refetch while already paused', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText('status: loading, fetchStatus: paused'), + ) + + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + + await sleep(15) + + // invalidation should not trigger a refetch + await waitFor(() => + screen.getByText('status: loading, fetchStatus: paused'), + ) + + expect(count).toBe(0) + onlineMock.mockRestore() + }) + + it('online queries should not refetch while already paused if data is in the cache', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + initialData: 'initial', + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: paused'), + ) + await waitFor(() => { + expect(screen.getByText('data: initial')).toBeInTheDocument() + }) + + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + + await sleep(15) + + // invalidation should not trigger a refetch + await waitFor(() => + screen.getByText('status: success, fetchStatus: paused'), + ) + + expect(count).toBe(0) + onlineMock.mockRestore() + }) + + it('online queries should not get stuck in fetching state when pausing multiple times', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + initialData: 'initial', + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+ +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: paused'), + ) + await waitFor(() => { + expect(screen.getByText('data: initial')).toBeInTheDocument() + }) + + // triggers one pause + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + + await sleep(15) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: paused'), + ) + + // triggers a second pause + window.dispatchEvent(new FocusEvent('focus')) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: idle'), + ) + await waitFor(() => { + expect(screen.getByText('data: data1')).toBeInTheDocument() + }) + + expect(count).toBe(1) + onlineMock.mockRestore() + }) + + it('online queries should pause retries if you are offline', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { + count++ + await sleep(10) + throw new Error('failed' + count) + }, + retry: 2, + retryDelay: 10, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus}, + failureCount: {state.failureCount} +
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText( + 'status: loading, fetchStatus: fetching, failureCount: 1', + ), + ) + + const onlineMock = mockNavigatorOnLine(false) + + await sleep(20) + + await waitFor(() => + screen.getByText( + 'status: loading, fetchStatus: paused, failureCount: 1', + ), + ) + + expect(count).toBe(1) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + screen.getByText('status: error, fetchStatus: idle, failureCount: 3'), + ) + + expect(count).toBe(3) + + onlineMock.mockRestore() + }) + + it('online queries should fetch if paused and we go online even if already unmounted (because not cancelled)', async () => { + const key = queryKey() + let count = 0 + + function Component() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+
+ ) + } + + function Page() { + const [show, setShow] = createSignal(true) + + return ( +
+ {show() && } + +
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText('status: loading, fetchStatus: paused'), + ) + + fireEvent.click(screen.getByRole('button', { name: /hide/i })) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(15) + + expect(queryClient.getQueryState(key())).toMatchObject({ + fetchStatus: 'idle', + status: 'success', + }) + + expect(count).toBe(1) + + onlineMock.mockRestore() + }) + + it('online queries should not fetch if paused and we go online when cancelled and no refetchOnReconnect', async () => { + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data' + count + }, + refetchOnReconnect: false, + }) + + return ( +
+ +
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+
+ ) + } + + const onlineMock = mockNavigatorOnLine(false) + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText('status: loading, fetchStatus: paused'), + ) + + fireEvent.click(screen.getByRole('button', { name: /cancel/i })) + + await waitFor(() => + screen.getByText('status: loading, fetchStatus: idle'), + ) + + expect(count).toBe(0) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(15) + + await waitFor(() => + screen.getByText('status: loading, fetchStatus: idle'), + ) + + expect(count).toBe(0) + + onlineMock.mockRestore() + }) + + it('online queries should not fetch if paused and we go online if already unmounted when signal consumed', async () => { + const key = queryKey() + let count = 0 + + function Component() { + const state = createQuery({ + queryKey: key, + queryFn: async ({ signal }) => { + count++ + await sleep(10) + return `${signal ? 'signal' : 'data'}${count}` + }, + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus} +
+
data: {state.data}
+
+ ) + } + + function Page() { + const [show, setShow] = createSignal(true) + + return ( +
+ {show() && } + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: idle'), + ) + + const onlineMock = mockNavigatorOnLine(false) + + fireEvent.click(screen.getByRole('button', { name: /invalidate/i })) + + await waitFor(() => + screen.getByText('status: success, fetchStatus: paused'), + ) + + fireEvent.click(screen.getByRole('button', { name: /hide/i })) + + await sleep(15) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await sleep(15) + + expect(queryClient.getQueryState(key())).toMatchObject({ + fetchStatus: 'idle', + status: 'success', + }) + + expect(count).toBe(typeof AbortSignal === 'function' ? 1 : 2) + + onlineMock.mockRestore() + }) + }) + + describe('networkMode always', () => { + it('always queries should start fetching even if you are offline', async () => { + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async () => { + count++ + await sleep(10) + return 'data ' + count + }, + networkMode: 'always', + }) + + return ( +
+
+ status: {state.status}, isPaused: {String(state.isPaused)} +
+
data: {state.data}
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('status: success, isPaused: false')) + + await waitFor(() => { + expect(screen.getByText('data: data 1')).toBeInTheDocument() + }) + + onlineMock.mockRestore() + }) + + it('always queries should not pause retries', async () => { + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { + count++ + await sleep(10) + throw new Error('error ' + count) + }, + networkMode: 'always', + retry: 1, + retryDelay: 5, + }) + + return ( +
+
+ status: {state.status}, isPaused: {String(state.isPaused)} +
+
+ error: {state.error instanceof Error && state.error.message} +
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('status: error, isPaused: false')) + + await waitFor(() => { + expect(screen.getByText('error: error 2')).toBeInTheDocument() + }) + + expect(count).toBe(2) + + onlineMock.mockRestore() + }) + }) + + describe('networkMode offlineFirst', () => { + it('offlineFirst queries should start fetching if you are offline, but pause retries', async () => { + const onlineMock = mockNavigatorOnLine(false) + + const key = queryKey() + let count = 0 + + function Page() { + const state = createQuery({ + queryKey: key, + queryFn: async (): Promise => { + count++ + await sleep(10) + throw new Error('failed' + count) + }, + retry: 2, + retryDelay: 1, + networkMode: 'offlineFirst', + }) + + return ( +
+
+ status: {state.status}, fetchStatus: {state.fetchStatus}, + failureCount: {state.failureCount} +
+
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => + screen.getByText( + 'status: loading, fetchStatus: paused, failureCount: 1', + ), + ) + + expect(count).toBe(1) + + onlineMock.mockReturnValue(true) + window.dispatchEvent(new Event('online')) + + await waitFor(() => + screen.getByText('status: error, fetchStatus: idle, failureCount: 3'), + ) + + expect(count).toBe(3) + + onlineMock.mockRestore() + }) + }) + + it('it should have status=error on mount when a query has failed', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + const error = new Error('oops') + + const queryFn = async (): Promise => { + throw error + } + + function Page() { + const state = createQuery(key, queryFn, { + retry: false, + retryOnMount: false, + }) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return <> + } + + await queryClient.prefetchQuery(key(), queryFn) + render(() => ( + + + + )) + + await waitFor(() => expect(states).toHaveLength(1)) + + expect(states[0]).toMatchObject({ + status: 'error', + error, + }) + }) + + it('setQueryData - should not call onSuccess callback of active observers', async () => { + const key = queryKey() + const onSuccess = jest.fn() + + function Page() { + const state = createQuery(key, () => 'data', { onSuccess }) + return ( +
+
data: {state.data}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: data')) + fireEvent.click(screen.getByRole('button', { name: /setQueryData/i })) + await waitFor(() => screen.getByText('data: newData')) + + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(onSuccess).toHaveBeenCalledWith('data') + }) + + it('setQueryData - should respect updatedAt', async () => { + const key = queryKey() + + function Page() { + const state = createQuery(key, () => 'data') + return ( +
+
data: {state.data}
+
dataUpdatedAt: {state.dataUpdatedAt}
+ +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('data: data')) + fireEvent.click(screen.getByRole('button', { name: /setQueryData/i })) + await waitFor(() => screen.getByText('data: newData')) + await waitFor(() => { + expect(screen.getByText('dataUpdatedAt: 100')).toBeInTheDocument() + }) + }) + + it('errorUpdateCount should increased on each fetch failure', async () => { + const key = queryKey() + const error = new Error('oops') + + function Page() { + const state = createQuery( + key, + async (): Promise => { + throw error + }, + { + retry: false, + }, + ) + return ( +
+ + data: {state.errorUpdateCount} +
+ ) + } + render(() => ( + + + + )) + const fetchBtn = screen.getByRole('button', { name: 'refetch' }) + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(fetchBtn) + await waitFor(() => screen.getByText('data: 2')) + fireEvent.click(fetchBtn) + await waitFor(() => screen.getByText('data: 3')) + }) +}) diff --git a/packages/solid-query/src/__tests__/createQuery.types.test.tsx b/packages/solid-query/src/__tests__/createQuery.types.test.tsx new file mode 100644 index 0000000000..1d82484d5b --- /dev/null +++ b/packages/solid-query/src/__tests__/createQuery.types.test.tsx @@ -0,0 +1,160 @@ +import { createQuery } from '../createQuery' + +export type Equal = (() => T extends X ? 1 : 2) extends < + T, +>() => T extends Y ? 1 : 2 + ? true + : false + +export type Expect = T + +const doNotExecute = (_func: () => void) => true + +describe('initialData', () => { + describe('Config object overload', () => { + it('TData should always be defined when initialData is provided as an object', () => { + doNotExecute(() => { + const { data } = createQuery({ + queryFn: () => { + return { + wow: true, + } + }, + initialData: { + wow: true, + }, + }) + + const result: Expect> = true + return result + }) + }) + + it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { + doNotExecute(() => { + const { data } = createQuery({ + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => ({ + wow: true, + }), + }) + + const result: Expect> = true + return result + }) + }) + + it('TData should have undefined in the union when initialData is NOT provided', () => { + doNotExecute(() => { + const { data } = createQuery({ + queryFn: () => { + return { + wow: true, + } + }, + }) + + const result: Expect> = + true + return result + }) + }) + + it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { + doNotExecute(() => { + const { data } = createQuery({ + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + }) + + const result: Expect> = + true + return result + }) + }) + }) + + describe('Query key overload', () => { + it('TData should always be defined when initialData is provided', () => { + doNotExecute(() => { + const { data } = createQuery(() => ['key'], { + queryFn: () => { + return { + wow: true, + } + }, + initialData: { + wow: true, + }, + }) + + const result: Expect> = true + return result + }) + }) + + it('TData should have undefined in the union when initialData is NOT provided', () => { + doNotExecute(() => { + const { data } = createQuery(() => ['key'], { + queryFn: () => { + return { + wow: true, + } + }, + }) + + const result: Expect> = + true + return result + }) + }) + }) + + describe('Query key and func', () => { + it('TData should always be defined when initialData is provided', () => { + doNotExecute(() => { + const { data } = createQuery( + () => ['key'], + () => { + return { + wow: true, + } + }, + { + initialData: { + wow: true, + }, + }, + ) + + const result: Expect> = true + return result + }) + }) + + it('TData should have undefined in the union when initialData is NOT provided', () => { + doNotExecute(() => { + const { data } = createQuery( + () => ['key'], + () => { + return { + wow: true, + } + }, + ) + + const result: Expect> = + true + return result + }) + }) + }) +}) diff --git a/packages/solid-query/src/__tests__/suspense.test.tsx b/packages/solid-query/src/__tests__/suspense.test.tsx new file mode 100644 index 0000000000..e95b488439 --- /dev/null +++ b/packages/solid-query/src/__tests__/suspense.test.tsx @@ -0,0 +1,1093 @@ +import { fireEvent, render, screen, waitFor } from 'solid-testing-library' + +import { + createRenderEffect, + createSignal, + ErrorBoundary, + on, + Show, + Suspense, +} from 'solid-js' +import type { CreateInfiniteQueryResult, CreateQueryResult } from '..' +import { + createInfiniteQuery, + createQuery, + QueryCache, + QueryClientProvider, +} from '..' +import { createQueryClient, queryKey, sleep } from './utils' + +describe("useQuery's in Suspense mode", () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + it('should render the correct amount of times in Suspense mode', async () => { + const key = queryKey() + const states: CreateQueryResult[] = [] + + let count = 0 + let renders = 0 + + function Page() { + const [stateKey, setStateKey] = createSignal(key()) + + const state = createQuery( + stateKey, + async () => { + count++ + await sleep(10) + return count + }, + { suspense: true }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + createRenderEffect( + on([() => ({ ...state }), key], () => { + renders++ + }), + ) + + return ( +
+
+ ) + } + + render(() => ( + + + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + fireEvent.click(screen.getByLabelText('toggle')) + + await waitFor(() => screen.getByText('data: 2')) + + expect(renders).toBe(4) + // TODO(lukemurray): verify that this expectation is valid. this is 2 in + // react, but 4 in solid, because in solid suspense is triggered on read and + // the component needs to render in order to trigger suspense. + expect(states.length).toBe(4) + expect(states[1]).toMatchObject({ data: 1, status: 'success' }) + expect(states[3]).toMatchObject({ data: 2, status: 'success' }) + }) + + it('should return the correct states for a successful infinite query', async () => { + const key = queryKey() + const states: CreateInfiniteQueryResult[] = [] + + function Page() { + const [multiplier, setMultiplier] = createSignal(1) + const state = createInfiniteQuery( + () => [`${key()}_${multiplier()}`], + async ({ pageParam = 1 }) => { + await sleep(10) + return Number(pageParam * multiplier()) + }, + { + suspense: true, + getNextPageParam: (lastPage) => lastPage + 1, + }, + ) + + createRenderEffect(() => { + states.push({ ...state }) + }) + + return ( +
+ + data: {state.data?.pages.join(',')} +
+ ) + } + + render(() => ( + + + + + + )) + + await waitFor(() => screen.getByText('data: 1')) + + // TODO(lukemurray): in react this is 1 in solid this is 2 because suspense + // occurs on read. + expect(states.length).toBe(2) + expect(states[1]).toMatchObject({ + data: { pages: [1], pageParams: [undefined] }, + status: 'success', + }) + + fireEvent.click(screen.getByText('next')) + await waitFor(() => screen.getByText('data: 2')) + + // TODO(lukemurray): in react this is 2 and in solid it is 4 + expect(states.length).toBe(4) + expect(states[3]).toMatchObject({ + data: { pages: [2], pageParams: [undefined] }, + status: 'success', + }) + }) + + it('should not call the queryFn twice when used in Suspense mode', async () => { + const key = queryKey() + + const queryFn = jest.fn() + queryFn.mockImplementation(() => { + sleep(10) + return 'data' + }) + + function Page() { + createQuery(() => [key()], queryFn, { suspense: true }) + + return <>rendered + } + + render(() => ( + + + + + + )) + + await waitFor(() => screen.getByText('rendered')) + + expect(queryFn).toHaveBeenCalledTimes(1) + }) + + it('should remove query instance when component unmounted', async () => { + const key = queryKey() + + function Page() { + createQuery( + key, + () => { + sleep(10) + return 'data' + }, + { suspense: true }, + ) + + return <>rendered + } + + function App() { + const [show, setShow] = createSignal(false) + + return ( + <> + {show() && } + + + )} + > + + + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + + await waitFor(() => screen.getByText('error boundary')) + + await waitFor(() => screen.getByText('retry')) + + fireEvent.click(screen.getByText('retry')) + + await waitFor(() => screen.getByText('rendered')) + }) + + it('should retry fetch if the reset error boundary has been reset', async () => { + const key = queryKey() + + let succeed = false + + function Page() { + const state = createQuery( + key, + async () => { + await sleep(10) + if (!succeed) { + throw new Error('Suspense Error Bingo') + } else { + return 'data' + } + }, + { + retry: false, + suspense: true, + }, + ) + + // Suspense only triggers if used in JSX + return ( + +
rendered
+
+ ) + } + + render(() => ( + + ( +
+
error boundary
+ +
+ )} + > + + + +
+
+ )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText('error boundary')) + await waitFor(() => screen.getByText('retry')) + fireEvent.click(screen.getByText('retry')) + await waitFor(() => screen.getByText('error boundary')) + await waitFor(() => screen.getByText('retry')) + succeed = true + fireEvent.click(screen.getByText('retry')) + await waitFor(() => screen.getByText('rendered')) + }) + + it('should refetch when re-mounting', async () => { + const key = queryKey() + let count = 0 + + function Component() { + const result = createQuery( + key, + async () => { + await sleep(100) + count++ + return count + }, + { + retry: false, + suspense: true, + staleTime: 0, + }, + ) + return ( +
+ data: {result.data} + fetching: {result.isFetching ? 'true' : 'false'} +
+ ) + } + + function Page() { + const [show, setShow] = createSignal(true) + return ( +
+ + {show() && } +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText('data: 1')) + await waitFor(() => screen.getByText('fetching: false')) + await waitFor(() => screen.getByText('hide')) + fireEvent.click(screen.getByText('hide')) + await waitFor(() => screen.getByText('show')) + fireEvent.click(screen.getByText('show')) + await waitFor(() => screen.getByText('fetching: true')) + await waitFor(() => screen.getByText('data: 2')) + await waitFor(() => screen.getByText('fetching: false')) + }) + + it('should suspend when switching to a new query', async () => { + const key1 = queryKey() + const key2 = queryKey() + + function Component(props: { queryKey: Array }) { + const result = createQuery( + () => props.queryKey, + async () => { + await sleep(100) + return props.queryKey + }, + { + retry: false, + suspense: true, + }, + ) + return
data: {result.data}
+ } + + function Page() { + const [key, setKey] = createSignal(key1()) + return ( +
+ + + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText(`data: ${key1()}`)) + fireEvent.click(screen.getByText('switch')) + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText(`data: ${key2()}`)) + expect( + // @ts-expect-error + queryClient.getQueryCache().find(key2())!.observers[0].listeners.length, + ).toBe(1) + }) + + it('should throw errors to the error boundary by default', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + async (): Promise => { + await sleep(10) + throw new Error('Suspense Error a1x') + }, + { + retry: false, + suspense: true, + }, + ) + + // read state.data to trigger suspense. + createRenderEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- trigger suspense + state.data + }) + + return
rendered
+ } + + function App() { + return ( + ( +
+
error boundary
+
+ )} + > + + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText('error boundary')) + }) + + it('should not throw errors to the error boundary when useErrorBoundary: false', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + async (): Promise => { + await sleep(10) + throw new Error('Suspense Error a2x') + }, + { + retry: false, + suspense: true, + useErrorBoundary: false, + }, + ) + + // read state.data to trigger suspense. + createRenderEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- trigger suspense + state.data + }) + + return
rendered
+ } + + function App() { + return ( + ( +
+
error boundary
+
+ )} + > + + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText('rendered')) + }) + + it('should not throw errors to the error boundary when a useErrorBoundary function returns true', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + async (): Promise => { + await sleep(10) + return Promise.reject('Remote Error') + }, + { + retry: false, + suspense: true, + useErrorBoundary: (err) => err !== 'Local Error', + }, + ) + + // read state.data to trigger suspense. + createRenderEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- trigger suspense + state.data + }) + + return
rendered
+ } + + function App() { + return ( + ( +
+
error boundary
+
+ )} + > + + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText('error boundary')) + }) + + it('should not throw errors to the error boundary when a useErrorBoundary function returns false', async () => { + const key = queryKey() + + function Page() { + const state = createQuery( + key, + async (): Promise => { + await sleep(10) + return Promise.reject('Local Error') + }, + { + retry: false, + suspense: true, + useErrorBoundary: (err) => err !== 'Local Error', + }, + ) + + // read state.data to trigger suspense. + createRenderEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- trigger suspense + state.data + }) + + return
rendered
+ } + + function App() { + return ( + ( +
+
error boundary
+
+ )} + > + + + +
+ ) + } + + render(() => ( + + + + )) + + await waitFor(() => screen.getByText('Loading...')) + await waitFor(() => screen.getByText('rendered')) + }) + + it('should not call the queryFn when not enabled', async () => { + const key = queryKey() + + const queryFn = jest.fn, unknown[]>() + queryFn.mockImplementation(async () => { + await sleep(10) + return '23' + }) + + function Page() { + const [enabled, setEnabled] = createSignal(false) + const result = createQuery(() => [key()], queryFn, { + suspense: true, + get enabled() { + return enabled() + }, + }) + + return ( +
+ +

{result.data}

+
+ ) + } + + render(() => ( + + + + + + )) + + expect(queryFn).toHaveBeenCalledTimes(0) + await sleep(5) + fireEvent.click(screen.getByRole('button', { name: /fire/i })) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('23') + }) + + expect(queryFn).toHaveBeenCalledTimes(1) + }) + + it('should not render suspense fallback when not enabled', async () => { + const key = queryKey() + + function Page() { + const [enabled, setEnabled] = createSignal(false) + const result = createQuery( + key, + () => { + sleep(10) + return 'data' + }, + { + suspense: true, + get enabled() { + return enabled() + }, + }, + ) + + return ( +
+ +

{result.data ?? 'default'}

+
+ ) + } + + render(() => ( + + + + + + )) + + expect(screen.queryByText('Loading...')).toBeNull() + expect(screen.getByRole('heading').textContent).toBe('default') + await sleep(5) + fireEvent.click(screen.getByRole('button', { name: /fire/i })) + await waitFor(() => screen.getByText('Loading...')) + + await waitFor(() => { + expect(screen.getByRole('heading').textContent).toBe('data') + }) + }) + + it('should error catched in error boundary without infinite loop', async () => { + const key = queryKey() + + let succeed = true + + function Page() { + const [nonce] = createSignal(0) + const queryKeys = () => [`${key()}-${succeed}`] + const result = createQuery( + queryKeys, + async () => { + await sleep(10) + if (!succeed) { + throw new Error('Suspense Error Bingo') + } else { + return nonce() + } + }, + { + retry: false, + suspense: true, + }, + ) + return ( +
+ rendered {result.data} + +
+ ) + } + + function App() { + return ( +
error boundary
}> + + + +
+ ) + } + + render(() => ( + + + + )) + + // render suspense fallback (Loading...) + await waitFor(() => screen.getByText('Loading...')) + // resolve promise -> render Page (rendered) + await waitFor(() => screen.getByText('rendered')) + + // change query key + succeed = false + // reset query -> and throw error + fireEvent.click(screen.getByLabelText('fail')) + // render error boundary fallback (error boundary) + await waitFor(() => screen.getByText('error boundary')) + }) + + it('should error catched in error boundary without infinite loop when query keys changed', async () => { + let succeed = true + + function Page() { + const [key, setKey] = createSignal(0) + + const result = createQuery( + () => [`${key()}-${succeed}`], + async () => { + await sleep(10) + if (!succeed) { + throw new Error('Suspense Error Bingo') + } else { + return 'data' + } + }, + { + retry: false, + suspense: true, + }, + ) + return ( +
+ rendered {result.data} + +
+ ) + } + + function App() { + return ( +
error boundary
}> + + + +
+ ) + } + + render(() => ( + + + + )) + + // render suspense fallback (Loading...) + await waitFor(() => screen.getByText('Loading...')) + // resolve promise -> render Page (rendered) + await waitFor(() => screen.getByText('rendered')) + + // change promise result to error + succeed = false + // change query key + fireEvent.click(screen.getByLabelText('fail')) + // render error boundary fallback (error boundary) + await waitFor(() => screen.getByText('error boundary')) + }) + + it('should error catched in error boundary without infinite loop when enabled changed', async () => { + function Page() { + const queryKeys = '1' + const [enabled, setEnabled] = createSignal(false) + + const result = createQuery( + () => [queryKeys], + async () => { + await sleep(10) + throw new Error('Suspense Error Bingo') + }, + { + retry: false, + suspense: true, + get enabled() { + return enabled() + }, + }, + ) + return ( +
+ rendered {result.data} + +
+ ) + } + + function App() { + return ( +
error boundary
}> + + + +
+ ) + } + + render(() => ( + + + + )) + + // render empty data with 'rendered' when enabled is false + await waitFor(() => screen.getByText('rendered')) + + // change enabled to true + fireEvent.click(screen.getByLabelText('fail')) + + // render pending fallback + await waitFor(() => screen.getByText('Loading...')) + + // render error boundary fallback (error boundary) + await waitFor(() => screen.getByText('error boundary')) + }) + + it('should render the correct amount of times in Suspense mode when cacheTime is set to 0', async () => { + const key = queryKey() + let state: CreateQueryResult | null = null + + let count = 0 + let renders = 0 + + function Page() { + state = createQuery( + key, + async () => { + count++ + await sleep(10) + return count + }, + { suspense: true, cacheTime: 0 }, + ) + + createRenderEffect( + on([() => ({ ...state })], () => { + renders++ + }), + ) + + return ( +
+ rendered +
+ ) + } + + render(() => ( + + + + + + )) + + await waitFor(() => + expect(state).toMatchObject({ + data: 1, + status: 'success', + }), + ) + + expect(renders).toBe(2) + expect(screen.queryByText('rendered')).not.toBeNull() + }) +}) diff --git a/packages/solid-query/src/__tests__/useIsFetching.test.tsx b/packages/solid-query/src/__tests__/useIsFetching.test.tsx new file mode 100644 index 0000000000..2099a385ef --- /dev/null +++ b/packages/solid-query/src/__tests__/useIsFetching.test.tsx @@ -0,0 +1,298 @@ +import { fireEvent, render, screen, waitFor } from 'solid-testing-library' + +import { + createContext, + createEffect, + createRenderEffect, + createSignal, + ErrorBoundary, + Show, +} from 'solid-js' +import type { QueryClient } from '..' +import { createQuery, QueryCache, QueryClientProvider, useIsFetching } from '..' +import { createQueryClient, queryKey, setActTimeout, sleep } from './utils' + +describe('useIsFetching', () => { + // See https://github.com/tannerlinsley/react-query/issues/105 + it('should update as queries start and stop fetching', async () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + const key = queryKey() + + function IsFetching() { + const isFetching = useIsFetching() + return
isFetching: {isFetching()}
+ } + + function Query() { + const [ready, setReady] = createSignal(false) + + createQuery( + key, + async () => { + await sleep(50) + return 'test' + }, + { + get enabled() { + return ready() + }, + }, + ) + + return + } + + function Page() { + return ( +
+ + +
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('isFetching: 0') + fireEvent.click(screen.getByRole('button', { name: /setReady/i })) + await screen.findByText('isFetching: 1') + await screen.findByText('isFetching: 0') + }) + + it('should not update state while rendering', async () => { + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + + const key1 = queryKey() + const key2 = queryKey() + + const isFetchings: number[] = [] + + function IsFetching() { + const isFetching = useIsFetching() + createRenderEffect(() => { + isFetchings.push(isFetching()) + }) + return null + } + + function FirstQuery() { + createQuery(key1, async () => { + await sleep(100) + return 'data' + }) + return null + } + + function SecondQuery() { + createQuery(key2, async () => { + await sleep(100) + return 'data' + }) + return null + } + + function Page() { + const [renderSecond, setRenderSecond] = createSignal(false) + + createEffect(() => { + setActTimeout(() => { + setRenderSecond(true) + }, 50) + }) + + return ( + <> + + + + + + + ) + } + + render(() => ( + + + + )) + // unlike react, Updating renderSecond wont cause a rerender for FirstQuery + await waitFor(() => expect(isFetchings).toEqual([0, 1, 2, 1, 0])) + }) + + it('should be able to filter', async () => { + const queryClient = createQueryClient() + const key1 = queryKey() + const key2 = queryKey() + + const isFetchings: number[] = [] + + function One() { + createQuery(key1, async () => { + await sleep(10) + return 'test' + }) + return null + } + + function Two() { + createQuery(key2, async () => { + await sleep(20) + return 'test' + }) + return null + } + + function Page() { + const [started, setStarted] = createSignal(false) + const isFetching = useIsFetching(key1) + + createRenderEffect(() => { + isFetchings.push(isFetching()) + }) + + return ( +
+ +
isFetching: {isFetching()}
+ + <> + + + + +
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('isFetching: 0') + fireEvent.click(screen.getByRole('button', { name: /setStarted/i })) + await screen.findByText('isFetching: 1') + await screen.findByText('isFetching: 0') + // at no point should we have isFetching: 2 + expect(isFetchings).toEqual(expect.not.arrayContaining([2])) + }) + + describe('with custom context', () => { + it('should update as queries start and stop fetching', async () => { + const context = createContext(undefined) + + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + const key = queryKey() + + function Page() { + const [ready, setReady] = createSignal(false) + + const isFetching = useIsFetching(undefined, { context: context }) + + createQuery( + key, + async () => { + await sleep(50) + return 'test' + }, + { + get enabled() { + return ready() + }, + context, + }, + ) + + return ( +
+
isFetching: {isFetching}
+ +
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('isFetching: 0') + fireEvent.click(screen.getByRole('button', { name: /setReady/i })) + await screen.findByText('isFetching: 1') + await screen.findByText('isFetching: 0') + }) + + it('should throw if the context is not passed to useIsFetching', async () => { + const context = createContext(undefined) + + const queryCache = new QueryCache() + const queryClient = createQueryClient({ queryCache }) + const key = queryKey() + + function Page() { + const isFetching = useIsFetching() + + createQuery(key, async () => 'test', { + enabled: true, + context, + useErrorBoundary: true, + }) + + return ( +
+
isFetching: {isFetching}
+
+ ) + } + + render(() => ( + +
error boundary
}> + +
+
+ )) + + await waitFor(() => screen.getByText('error boundary')) + }) + }) + + it('should show the correct fetching state when mounted after a query', async () => { + const queryClient = createQueryClient() + const key = queryKey() + + function Page() { + createQuery(key, async () => { + await sleep(10) + return 'test' + }) + + const isFetching = useIsFetching() + + return ( +
+
isFetching: {isFetching()}
+
+ ) + } + + render(() => ( + + + + )) + + await screen.findByText('isFetching: 1') + await screen.findByText('isFetching: 0') + }) +}) diff --git a/packages/solid-query/src/__tests__/useIsMutating.test.tsx b/packages/solid-query/src/__tests__/useIsMutating.test.tsx new file mode 100644 index 0000000000..64e1f9f6dd --- /dev/null +++ b/packages/solid-query/src/__tests__/useIsMutating.test.tsx @@ -0,0 +1,301 @@ +import { fireEvent, screen, waitFor } from 'solid-testing-library' +import { + createMutation, + QueryClient, + QueryClientProvider, + useIsMutating, +} from '..' +import { createQueryClient, sleep } from './utils' + +import { + createContext, + createEffect, + createRenderEffect, + createSignal, + ErrorBoundary, + Show, +} from 'solid-js' +import { render } from 'solid-testing-library' +import * as MutationCacheModule from '../../../query-core/src/mutationCache' +import { setActTimeout } from './utils' + +describe('useIsMutating', () => { + it('should return the number of fetching mutations', async () => { + const isMutatings: number[] = [] + const queryClient = createQueryClient() + + function IsMutating() { + const isMutating = useIsMutating() + createRenderEffect(() => { + isMutatings.push(isMutating()) + }) + return null + } + + function Mutations() { + const { mutate: mutate1 } = createMutation(['mutation1'], async () => { + await sleep(150) + return 'data' + }) + const { mutate: mutate2 } = createMutation(['mutation2'], async () => { + await sleep(50) + return 'data' + }) + + createEffect(() => { + mutate1() + setActTimeout(() => { + mutate2() + }, 50) + }) + + return null + } + + function Page() { + return ( +
+ + +
+ ) + } + + render(() => ( + + + + )) + await waitFor(() => expect(isMutatings).toEqual([0, 1, 2, 1, 0])) + }) + + it('should filter correctly by mutationKey', async () => { + const isMutatings: number[] = [] + const queryClient = createQueryClient() + + function IsMutating() { + const isMutating = useIsMutating(['mutation1']) + createRenderEffect(() => { + isMutatings.push(isMutating()) + }) + return null + } + + function Page() { + const { mutate: mutate1 } = createMutation(['mutation1'], async () => { + await sleep(100) + return 'data' + }) + const { mutate: mutate2 } = createMutation(['mutation2'], async () => { + await sleep(100) + return 'data' + }) + + createEffect(() => { + mutate1() + mutate2() + }) + + return + } + + render(() => ( + + + + )) + // Unlike React, IsMutating Wont re-render twice with mutation2 + await waitFor(() => expect(isMutatings).toEqual([0, 1, 0])) + }) + + it('should filter correctly by predicate', async () => { + const isMutatings: number[] = [] + const queryClient = createQueryClient() + + function IsMutating() { + const isMutating = useIsMutating({ + predicate: (mutation) => + mutation.options.mutationKey?.[0] === 'mutation1', + }) + createRenderEffect(() => { + isMutatings.push(isMutating()) + }) + return null + } + + function Page() { + const { mutate: mutate1 } = createMutation(['mutation1'], async () => { + await sleep(100) + return 'data' + }) + const { mutate: mutate2 } = createMutation(['mutation2'], async () => { + await sleep(100) + return 'data' + }) + + createEffect(() => { + mutate1() + mutate2() + }) + + return + } + + render(() => ( + + + + )) + + // Again, No unnecessary re-renders like React + await waitFor(() => expect(isMutatings).toEqual([0, 1, 0])) + }) + + it('should not change state if unmounted', async () => { + // We have to mock the MutationCache to not unsubscribe + // the listener when the component is unmounted + class MutationCacheMock extends MutationCacheModule.MutationCache { + subscribe(listener: any) { + super.subscribe(listener) + return () => void 0 + } + } + + const MutationCacheSpy = jest + .spyOn(MutationCacheModule, 'MutationCache') + .mockImplementation((fn) => { + return new MutationCacheMock(fn) + }) + + const queryClient = createQueryClient() + + function IsMutating() { + useIsMutating() + return null + } + + function Page() { + const [mounted, setMounted] = createSignal(true) + const { mutate: mutate1 } = createMutation(['mutation1'], async () => { + await sleep(10) + return 'data' + }) + + createEffect(() => { + mutate1() + }) + + return ( +
+ + + + +
+ ) + } + + render(() => ( + + + + )) + fireEvent.click(screen.getByText('unmount')) + + // Should not display the console error + // "Warning: Can't perform a React state update on an unmounted component" + + await sleep(20) + MutationCacheSpy.mockRestore() + }) + + describe('with custom context', () => { + it('should return the number of fetching mutations', async () => { + const context = createContext(undefined) + + const isMutatings: number[] = [] + const queryClient = new QueryClient() + + function IsMutating() { + const isMutating = useIsMutating(undefined, { context }) + + createRenderEffect(() => { + isMutatings.push(isMutating()) + }) + + return null + } + + function Page() { + const { mutate: mutate1 } = createMutation( + ['mutation1'], + async () => { + await sleep(150) + return 'data' + }, + { context }, + ) + const { mutate: mutate2 } = createMutation( + ['mutation2'], + async () => { + await sleep(50) + return 'data' + }, + { context }, + ) + + createEffect(() => { + mutate1() + setActTimeout(() => { + mutate2() + }, 50) + }) + + return + } + + render(() => ( + + + + )) + await waitFor(() => expect(isMutatings).toEqual([0, 1, 2, 1, 0])) + }) + + it('should throw if the context is not passed to useIsMutating', async () => { + const context = createContext(undefined) + + const isMutatings: number[] = [] + const queryClient = new QueryClient() + + function IsMutating() { + const isMutating = useIsMutating(undefined) + isMutatings.push(isMutating()) + return null + } + + function Page() { + const { mutate } = createMutation(['mutation'], async () => 'data', { + useErrorBoundary: true, + context, + }) + + createEffect(() => { + mutate() + }) + + return + } + + render(() => ( + +
error boundary
}> + +
+
+ )) + + await waitFor(() => screen.getByText('error boundary')) + }) + }) +}) diff --git a/packages/solid-query/src/__tests__/utils.tsx b/packages/solid-query/src/__tests__/utils.tsx new file mode 100644 index 0000000000..41c904f127 --- /dev/null +++ b/packages/solid-query/src/__tests__/utils.tsx @@ -0,0 +1,75 @@ +import type { QueryClientConfig } from '@tanstack/query-core' +import { QueryClient } from '@tanstack/query-core' +import type { ParentProps } from 'solid-js' +import { createEffect, createSignal, onCleanup, Show } from 'solid-js' + +let queryKeyCount = 0 +export function queryKey(): () => Array { + const localQueryKeyCount = queryKeyCount++ + return () => [`query_${localQueryKeyCount}`] +} + +export const Blink = ( + props: { + duration: number + } & ParentProps, +) => { + const [shouldShow, setShouldShow] = createSignal(true) + + createEffect(() => { + setShouldShow(true) + const timeout = setActTimeout(() => setShouldShow(false), props.duration) + onCleanup(() => clearTimeout(timeout)) + }) + + return ( + off}> + <>{props.children} + + ) +} + +export function createQueryClient(config?: QueryClientConfig): QueryClient { + jest.spyOn(console, 'error').mockImplementation(() => undefined) + return new QueryClient({ logger: mockLogger, ...config }) +} + +export function mockVisibilityState(value: DocumentVisibilityState) { + return jest.spyOn(document, 'visibilityState', 'get').mockReturnValue(value) +} + +export function mockNavigatorOnLine(value: boolean) { + return jest.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) +} + +export const mockLogger = { + log: jest.fn(), + warn: jest.fn(), + error: jest.fn(), +} + +export function sleep(timeout: number): Promise { + return new Promise((resolve, _reject) => { + setTimeout(resolve, timeout) + }) +} + +export function setActTimeout(fn: () => void, ms?: number) { + return setTimeout(() => { + fn() + }, ms) +} + +/** + * Assert the parameter is of a specific type. + */ +export function expectType(_: T): void { + return undefined +} + +/** + * Assert the parameter is not typed as `any` + */ +export function expectTypeNotAny(_: 0 extends 1 & T ? never : T): void { + return undefined +} diff --git a/packages/solid-query/src/createBaseQuery.ts b/packages/solid-query/src/createBaseQuery.ts new file mode 100644 index 0000000000..48fbebe209 --- /dev/null +++ b/packages/solid-query/src/createBaseQuery.ts @@ -0,0 +1,121 @@ +import type { QueryObserver } from '@tanstack/query-core' +import type { QueryKey, QueryObserverResult } from '@tanstack/query-core' +import type { CreateBaseQueryOptions } from './types' +import { useQueryClient } from './QueryClientProvider' +import { + onMount, + onCleanup, + createComputed, + createResource, + on, + batch, +} from 'solid-js' +import { createStore, unwrap } from 'solid-js/store' +import { shouldThrowError } from './utils' + +// Base Query Function that is used to create the query. +export function createBaseQuery< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey extends QueryKey, +>( + options: CreateBaseQueryOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + Observer: typeof QueryObserver, +): QueryObserverResult { + const queryClient = useQueryClient({ context: options.context }) + + const defaultedOptions = queryClient.defaultQueryOptions(options) + defaultedOptions._optimisticResults = 'optimistic' + const observer = new Observer(queryClient, defaultedOptions) + + const [state, setState] = createStore>( + // @ts-ignore + observer.getOptimisticResult(defaultedOptions), + ) + + const [dataResource, { refetch, mutate }] = createResource( + () => { + return new Promise((resolve) => { + if (!(state.isFetching && state.isLoading)) { + resolve(unwrap(state.data)) + } + }) + }, + ) + + batch(() => { + mutate(() => unwrap(state.data)) + refetch() + }) + + let taskQueue: Array<() => void> = [] + + const unsubscribe = observer.subscribe((result) => { + taskQueue.push(() => { + batch(() => { + setState(unwrap(result)) + mutate(() => unwrap(result.data)) + refetch() + }) + }) + + queueMicrotask(() => { + const taskToRun = taskQueue.pop() + if (taskToRun) { + taskToRun() + } + taskQueue = [] + }) + }) + + onCleanup(() => unsubscribe()) + + onMount(() => { + observer.setOptions(defaultedOptions, { listeners: false }) + }) + + createComputed(() => { + const newDefaultedOptions = queryClient.defaultQueryOptions(options) + observer.setOptions(newDefaultedOptions) + }) + + createComputed( + on( + () => state.status, + () => { + if ( + state.isError && + !state.isFetching && + shouldThrowError(observer.options.useErrorBoundary, [ + state.error, + observer.getCurrentQuery(), + ]) + ) { + throw state.error + } + }, + ), + ) + + const handler = { + get( + target: QueryObserverResult, + prop: keyof QueryObserverResult, + ): any { + if (prop === 'data' && target.isLoading && target.isFetching) { + return dataResource() + } + return Reflect.get(target, prop) + }, + } + + return new Proxy(state, handler) as QueryObserverResult +} diff --git a/packages/solid-query/src/createInfiniteQuery.ts b/packages/solid-query/src/createInfiniteQuery.ts new file mode 100644 index 0000000000..fbef03ea6c --- /dev/null +++ b/packages/solid-query/src/createInfiniteQuery.ts @@ -0,0 +1,116 @@ +import type { + QueryObserver, + QueryFunction, + QueryOptions, +} from '@tanstack/query-core' +import { InfiniteQueryObserver } from '@tanstack/query-core' +import type { + CreateInfiniteQueryOptions, + CreateInfiniteQueryResult, + SolidQueryKey, +} from './types' +import { createBaseQuery } from './createBaseQuery' +import { createComputed } from 'solid-js' +import { createStore } from 'solid-js/store' +import { parseQueryArgs } from './utils' + +export function createInfiniteQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + options: CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, +): CreateInfiniteQueryResult +export function createInfiniteQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + options?: Omit< + CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, + 'queryKey' + >, +): CreateInfiniteQueryResult +export function createInfiniteQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + queryFn: QueryFunction>, + options?: Omit< + CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, + 'queryKey' | 'queryFn' + >, +): CreateInfiniteQueryResult +export function createInfiniteQuery< + TQueryFnData, + TError, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + arg1: + | TQueryKey + | CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, + arg2?: + | QueryFunction> + | CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, + arg3?: CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + TQueryKey + >, +): CreateInfiniteQueryResult { + // The parseQuery Args functions helps normalize the arguments into the correct form. + // Whatever the parameters are, they are normalized into the correct form. + const [parsedOptions, setParsedOptions] = createStore( + parseQueryArgs(arg1, arg2, arg3), + ) + + // Watch for changes in the options and update the parsed options. + createComputed(() => { + const newParsedOptions = parseQueryArgs(arg1, arg2, arg3) + setParsedOptions(newParsedOptions) + }) + + return createBaseQuery( + parsedOptions as QueryOptions>, + InfiniteQueryObserver as typeof QueryObserver, + ) as CreateInfiniteQueryResult +} diff --git a/packages/solid-query/src/createMutation.ts b/packages/solid-query/src/createMutation.ts new file mode 100644 index 0000000000..2ecb4ad34d --- /dev/null +++ b/packages/solid-query/src/createMutation.ts @@ -0,0 +1,131 @@ +import type { MutationFunction, MutationKey } from '@tanstack/query-core' +import { parseMutationArgs, MutationObserver } from '@tanstack/query-core' +import { useQueryClient } from './QueryClientProvider' +import type { + CreateMutateFunction, + CreateMutationOptions, + CreateMutationResult, +} from './types' +import { createComputed, onCleanup, on } from 'solid-js' +import { createStore } from 'solid-js/store' +import { shouldThrowError } from './utils' + +// HOOK +export function createMutation< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +>( + options: CreateMutationOptions, +): CreateMutationResult +export function createMutation< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +>( + mutationFn: MutationFunction, + options?: Omit< + CreateMutationOptions, + 'mutationFn' + >, +): CreateMutationResult +export function createMutation< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +>( + mutationKey: MutationKey, + options?: Omit< + CreateMutationOptions, + 'mutationKey' + >, +): CreateMutationResult +export function createMutation< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +>( + mutationKey: MutationKey, + mutationFn?: MutationFunction, + options?: Omit< + CreateMutationOptions, + 'mutationKey' | 'mutationFn' + >, +): CreateMutationResult +export function createMutation< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +>( + arg1: + | MutationKey + | MutationFunction + | CreateMutationOptions, + arg2?: + | MutationFunction + | CreateMutationOptions, + arg3?: CreateMutationOptions, +): CreateMutationResult { + const [options, setOptions] = createStore(parseMutationArgs(arg1, arg2, arg3)) + const queryClient = useQueryClient({ context: options.context }) + + const observer = new MutationObserver( + queryClient, + options, + ) + + const mutate: CreateMutateFunction = ( + variables, + mutateOptions, + ) => { + observer.mutate(variables, mutateOptions).catch(noop) + } + + const [state, setState] = createStore< + CreateMutationResult + >({ + ...observer.getCurrentResult(), + mutate, + mutateAsync: observer.getCurrentResult().mutate, + }) + + createComputed(() => { + const newParsedOptions = parseMutationArgs(arg1, arg2, arg3) + setOptions(newParsedOptions) + observer.setOptions(newParsedOptions) + }) + + createComputed( + on( + () => state.status, + () => { + if ( + state.isError && + shouldThrowError(observer.options.useErrorBoundary, [state.error]) + ) { + throw state.error + } + }, + ), + ) + + const unsubscribe = observer.subscribe((result) => { + setState({ + ...result, + mutate, + mutateAsync: result.mutate, + }) + }) + + onCleanup(unsubscribe) + + return state +} + +// eslint-disable-next-line @typescript-eslint/no-empty-function +function noop() {} diff --git a/packages/solid-query/src/createQueries.ts b/packages/solid-query/src/createQueries.ts new file mode 100644 index 0000000000..1fc449641d --- /dev/null +++ b/packages/solid-query/src/createQueries.ts @@ -0,0 +1,203 @@ +import { createComputed, onCleanup, onMount } from 'solid-js' +import type { QueryFunction } from '@tanstack/query-core' +import { QueriesObserver } from '@tanstack/query-core' +import { useQueryClient } from './QueryClientProvider' +import type { + CreateQueryOptions, + CreateQueryResult, + SolidQueryKey, +} from './types' +import { createStore, unwrap } from 'solid-js/store' +import { scheduleMicrotask } from './utils' + +// This defines the `UseQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. +// - `context` is omitted as it is passed as a root-level option to `useQueries` instead. +type CreateQueryOptionsForCreateQueries< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +> = Omit, 'context'> + +// Avoid TS depth-limit error in case of large array literal +type MAXIMUM_DEPTH = 20 + +type GetOptions = + // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData } + T extends { + queryFnData: infer TQueryFnData + error?: infer TError + data: infer TData + } + ? CreateQueryOptionsForCreateQueries + : T extends { queryFnData: infer TQueryFnData; error?: infer TError } + ? CreateQueryOptionsForCreateQueries + : T extends { data: infer TData; error?: infer TError } + ? CreateQueryOptionsForCreateQueries + : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData] + T extends [infer TQueryFnData, infer TError, infer TData] + ? CreateQueryOptionsForCreateQueries + : T extends [infer TQueryFnData, infer TError] + ? CreateQueryOptionsForCreateQueries + : T extends [infer TQueryFnData] + ? CreateQueryOptionsForCreateQueries + : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided + T extends { + queryFn?: QueryFunction + select: (data: any) => infer TData + } + ? CreateQueryOptionsForCreateQueries< + TQueryFnData, + unknown, + TData, + () => TQueryKey + > + : T extends { queryFn?: QueryFunction } + ? CreateQueryOptionsForCreateQueries< + TQueryFnData, + unknown, + TQueryFnData, + () => TQueryKey + > + : // Fallback + CreateQueryOptionsForCreateQueries + +type GetResults = + // Part 1: responsible for mapping explicit type parameter to function result, if object + T extends { queryFnData: any; error?: infer TError; data: infer TData } + ? CreateQueryResult + : T extends { queryFnData: infer TQueryFnData; error?: infer TError } + ? CreateQueryResult + : T extends { data: infer TData; error?: infer TError } + ? CreateQueryResult + : // Part 2: responsible for mapping explicit type parameter to function result, if tuple + T extends [any, infer TError, infer TData] + ? CreateQueryResult + : T extends [infer TQueryFnData, infer TError] + ? CreateQueryResult + : T extends [infer TQueryFnData] + ? CreateQueryResult + : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided + T extends { + queryFn?: QueryFunction + select: (data: any) => infer TData + } + ? CreateQueryResult + : T extends { queryFn?: QueryFunction } + ? CreateQueryResult + : // Fallback + CreateQueryResult + +/** + * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param + */ +export type QueriesOptions< + T extends any[], + Result extends any[] = [], + Depth extends ReadonlyArray = [], +> = Depth['length'] extends MAXIMUM_DEPTH + ? CreateQueryOptionsForCreateQueries[] + : T extends [] + ? [] + : T extends [infer Head] + ? [...Result, GetOptions] + : T extends [infer Head, ...infer Tail] + ? QueriesOptions<[...Tail], [...Result, GetOptions], [...Depth, 1]> + : unknown[] extends T + ? T + : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type! + // use this to infer the param types in the case of Array.map() argument + T extends CreateQueryOptionsForCreateQueries< + infer TQueryFnData, + infer TError, + infer TData, + infer TQueryKey + >[] + ? CreateQueryOptionsForCreateQueries[] + : // Fallback + CreateQueryOptionsForCreateQueries[] + +/** + * QueriesResults reducer recursively maps type param to results + */ +export type QueriesResults< + T extends any[], + Result extends any[] = [], + Depth extends ReadonlyArray = [], +> = Depth['length'] extends MAXIMUM_DEPTH + ? CreateQueryResult[] + : T extends [] + ? [] + : T extends [infer Head] + ? [...Result, GetResults] + : T extends [infer Head, ...infer Tail] + ? QueriesResults<[...Tail], [...Result, GetResults], [...Depth, 1]> + : T extends CreateQueryOptionsForCreateQueries< + infer TQueryFnData, + infer TError, + infer TData, + any + >[] + ? // Dynamic-size (homogenous) UseQueryOptions array: map directly to array of results + CreateQueryResult[] + : // Fallback + CreateQueryResult[] + +type ArrType = T extends (infer U)[] ? U : never + +export function createQueries(queriesOptions: { + queries: readonly [...QueriesOptions] + context?: CreateQueryOptions['context'] +}): QueriesResults { + const queryClient = useQueryClient({ context: queriesOptions.context }) + + const normalizeOptions = ( + options: ArrType, + ) => { + const normalizedOptions = { ...options, queryKey: options.queryKey?.() } + const defaultedOptions = queryClient.defaultQueryOptions(normalizedOptions) + defaultedOptions._optimisticResults = 'optimistic' + return defaultedOptions + } + + const defaultedQueries = queriesOptions.queries.map((options) => + normalizeOptions(options), + ) + + const observer = new QueriesObserver(queryClient, defaultedQueries) + + const [state, setState] = createStore( + observer.getOptimisticResult(defaultedQueries), + ) + + const taskQueue: Array<() => void> = [] + + const unsubscribe = observer.subscribe((result) => { + taskQueue.push(() => { + setState(unwrap(result)) + }) + + scheduleMicrotask(() => { + const taskToRun = taskQueue.pop() + if (taskToRun) { + taskToRun() + taskQueue.splice(0, taskQueue.length) + } + }) + }) + + onCleanup(unsubscribe) + + onMount(() => { + observer.setQueries(defaultedQueries, { listeners: false }) + }) + + createComputed(() => { + const updateDefaultedQueries = queriesOptions.queries.map((options) => + normalizeOptions(options), + ) + observer.setQueries(updateDefaultedQueries) + }) + + return state as QueriesResults +} diff --git a/packages/solid-query/src/createQuery.ts b/packages/solid-query/src/createQuery.ts new file mode 100644 index 0000000000..387c63a543 --- /dev/null +++ b/packages/solid-query/src/createQuery.ts @@ -0,0 +1,157 @@ +import type { QueryFunction, QueryOptions } from '@tanstack/query-core' +import { QueryObserver } from '@tanstack/query-core' +import type { + CreateQueryOptions, + CreateQueryResult, + DefinedCreateQueryResult, + SolidQueryKey, +} from './types' +import { createComputed } from 'solid-js' +import { createStore } from 'solid-js/store' +import { parseQueryArgs } from './utils' +import { createBaseQuery } from './createBaseQuery' + +// There are several ways to create a query. +// 1. createQuery(options: CreateQueryOptions) +// 2. createQuery(querykey: () => Serializable[], options: CreateQueryOptions) +// 3. createQuery(querykey: () => Serializable[], queryFunc: Fetcher Function, options: CreateQueryOptions) +// 4. The fourth overload is a combination of all three function params +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + options: Omit< + CreateQueryOptions, + 'initialData' + > & { + initialData?: () => undefined + }, +): CreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + options: Omit< + CreateQueryOptions, + 'initialData' + > & { + initialData: TQueryFnData | (() => TQueryFnData) + }, +): DefinedCreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + options: CreateQueryOptions, +): CreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'initialData' + > & { initialData?: () => undefined }, +): CreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'initialData' + > & { initialData: TQueryFnData | (() => TQueryFnData) }, +): DefinedCreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + options?: Omit< + CreateQueryOptions, + 'queryKey' + >, +): CreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + // TODO(lukemurray): not sure if we want to use return type here + queryFn: QueryFunction>, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'queryFn' | 'initialData' + > & { initialData?: () => undefined }, +): CreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + queryFn: QueryFunction>, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'queryFn' | 'initialData' + > & { initialData: TQueryFnData | (() => TQueryFnData) }, +): DefinedCreateQueryResult +export function createQuery< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + queryKey: TQueryKey, + queryFn: QueryFunction>, + options?: Omit< + CreateQueryOptions, + 'queryKey' | 'queryFn' + >, +): CreateQueryResult +export function createQuery< + TQueryFnData, + TError, + TData = TQueryFnData, + TQueryKey extends SolidQueryKey = SolidQueryKey, +>( + arg1: TQueryKey | CreateQueryOptions, + arg2?: + | QueryFunction> + | CreateQueryOptions, + arg3?: CreateQueryOptions, +): CreateQueryResult { + // The parseQuery Args functions helps normalize the arguments into the correct form. + // Whatever the parameters are, they are normalized into the correct form. + const [parsedOptions, setParsedOptions] = createStore( + parseQueryArgs(arg1, arg2, arg3), + ) + + // Watch for changes in the options and update the parsed options. + createComputed(() => { + const newParsedOptions = parseQueryArgs(arg1, arg2, arg3) + setParsedOptions(newParsedOptions) + }) + + return createBaseQuery( + parsedOptions as QueryOptions>, + QueryObserver, + ) +} diff --git a/packages/solid-query/src/index.ts b/packages/solid-query/src/index.ts new file mode 100644 index 0000000000..81ba67caff --- /dev/null +++ b/packages/solid-query/src/index.ts @@ -0,0 +1,17 @@ +// Re-export core +export * from '@tanstack/query-core' + +// Solid Query +export * from './types' +export { createQuery } from './createQuery' +export { + defaultContext, + QueryClientProvider, + useQueryClient, +} from './QueryClientProvider' +export type { QueryClientProviderProps } from './QueryClientProvider' +export { useIsFetching } from './useIsFetching' +export { useIsMutating } from './useIsMutating' +export { createMutation } from './createMutation' +export { createInfiniteQuery } from './createInfiniteQuery' +export { createQueries } from './createQueries' diff --git a/packages/solid-query/src/types.ts b/packages/solid-query/src/types.ts new file mode 100644 index 0000000000..8895326156 --- /dev/null +++ b/packages/solid-query/src/types.ts @@ -0,0 +1,167 @@ +import type { Context } from 'solid-js' +import type { + QueryClient, + QueryKey, + QueryObserverOptions, + QueryObserverResult, + MutateFunction, + MutationObserverOptions, + MutationObserverResult, + DefinedQueryObserverResult, + InfiniteQueryObserverOptions, + InfiniteQueryObserverResult, + QueryFilters, + QueryOptions, +} from '@tanstack/query-core' + +export interface ContextOptions { + /** + * Use this to pass your Solid Query context. Otherwise, `defaultContext` will be used. + */ + context?: Context +} + +/* --- Create Query and Create Base Query Types --- */ +export type SolidQueryKey = () => readonly unknown[] + +export interface CreateBaseQueryOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> extends ContextOptions, + QueryObserverOptions {} + +export interface CreateQueryOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryKey extends () => readonly unknown[] = SolidQueryKey, +> extends Omit< + CreateBaseQueryOptions< + TQueryFnData, + TError, + TData, + TQueryFnData, + ReturnType + >, + 'queryKey' + > { + queryKey?: TQueryKey +} + +export type CreateBaseQueryResult< + TData = unknown, + TError = unknown, +> = QueryObserverResult + +export type CreateQueryResult< + TData = unknown, + TError = unknown, +> = CreateBaseQueryResult + +export type DefinedCreateBaseQueryResult< + TData = unknown, + TError = unknown, +> = DefinedQueryObserverResult + +export type DefinedCreateQueryResult< + TData = unknown, + TError = unknown, +> = DefinedCreateBaseQueryResult + +export type ParseQueryArgs< + TOptions extends Omit< + QueryOptions>, + 'queryKey' + > & { + queryKey?: TQueryKey + }, + TQueryKey extends () => readonly unknown[] = SolidQueryKey, +> = TOptions['queryKey'] extends () => infer R + ? Omit & { queryKey?: R } + : TOptions + +/* --- Create Infinite Queries Types --- */ +export interface CreateInfiniteQueryOptions< + TQueryFnData = unknown, + TError = unknown, + TData = TQueryFnData, + TQueryData = TQueryFnData, + TQueryKey extends () => readonly unknown[] = SolidQueryKey, +> extends ContextOptions, + Omit< + InfiniteQueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + ReturnType + >, + 'queryKey' + > { + queryKey?: TQueryKey +} + +export type CreateInfiniteQueryResult< + TData = unknown, + TError = unknown, +> = InfiniteQueryObserverResult + +/* --- Create Mutation Types --- */ +export interface CreateMutationOptions< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +> extends ContextOptions, + Omit< + MutationObserverOptions, + '_defaulted' | 'variables' + > {} + +export type CreateMutateFunction< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +> = ( + ...args: Parameters> +) => void + +export type CreateMutateAsyncFunction< + TData = unknown, + TError = unknown, + TVariables = void, + TContext = unknown, +> = MutateFunction + +export type CreateBaseMutationResult< + TData = unknown, + TError = unknown, + TVariables = unknown, + TContext = unknown, +> = Override< + MutationObserverResult, + { mutate: CreateMutateFunction } +> & { + mutateAsync: CreateMutateAsyncFunction +} + +export type CreateMutationResult< + TData = unknown, + TError = unknown, + TVariables = unknown, + TContext = unknown, +> = CreateBaseMutationResult + +type Override = { [K in keyof A]: K extends keyof B ? B[K] : A[K] } + +/* --- Use Is Fetching Types --- */ +export interface SolidQueryFilters extends Omit { + queryKey?: SolidQueryKey +} + +export type ParseFilterArgs = + T['queryKey'] extends () => infer R ? T & { queryKey: R } : T diff --git a/packages/solid-query/src/useIsFetching.ts b/packages/solid-query/src/useIsFetching.ts new file mode 100644 index 0000000000..06844a3796 --- /dev/null +++ b/packages/solid-query/src/useIsFetching.ts @@ -0,0 +1,58 @@ +import type { QueryFilters } from '@tanstack/query-core' + +import type { ContextOptions, SolidQueryKey, SolidQueryFilters } from './types' +import { useQueryClient } from './QueryClientProvider' +import type { Accessor } from 'solid-js' +import { createSignal, onCleanup, createComputed, createMemo } from 'solid-js' +import { parseFilterArgs } from './utils' + +interface Options extends ContextOptions {} + +export function useIsFetching( + filters?: SolidQueryFilters, + options?: Options, +): Accessor +export function useIsFetching( + queryKey?: SolidQueryKey, + filters?: SolidQueryFilters, + options?: Options, +): Accessor +export function useIsFetching( + arg1?: SolidQueryKey | SolidQueryFilters, + arg2?: SolidQueryFilters | Options, + arg3?: Options, +): Accessor { + const [filtersObj, optionsObj = {}] = parseFilterArgs(arg1, arg2, arg3) + + const [filters, setFilters] = createSignal(filtersObj) + const [options, setOptions] = createSignal(optionsObj) + + const queryClient = createMemo(() => + useQueryClient({ context: options().context }), + ) + const queryCache = createMemo(() => queryClient().getQueryCache()) + + const [fetches, setFetches] = createSignal( + queryClient().isFetching(filters as QueryFilters), + ) + + createComputed(() => { + const [newFiltersObj, newOptionsObj = {}] = parseFilterArgs( + arg1, + arg2, + arg3, + ) + setFilters(newFiltersObj) + setOptions(newOptionsObj) + }) + + const unsubscribe = queryCache().subscribe(() => { + setFetches(queryClient().isFetching(filters() as QueryFilters)) + }) + + onCleanup(() => { + unsubscribe() + }) + + return fetches +} diff --git a/packages/solid-query/src/useIsMutating.ts b/packages/solid-query/src/useIsMutating.ts new file mode 100644 index 0000000000..5fe0bc91ca --- /dev/null +++ b/packages/solid-query/src/useIsMutating.ts @@ -0,0 +1,42 @@ +import type { MutationKey, MutationFilters } from '@tanstack/query-core' +import { parseMutationFilterArgs } from '@tanstack/query-core' +import type { ContextOptions } from './types' +import { useQueryClient } from './QueryClientProvider' +import type { Accessor } from 'solid-js' +import { createSignal, onCleanup } from 'solid-js' + +interface Options extends ContextOptions {} + +export function useIsMutating( + filters?: MutationFilters, + options?: Options, +): Accessor +export function useIsMutating( + mutationKey?: MutationKey, + filters?: Omit, + options?: Options, +): Accessor +export function useIsMutating( + arg1?: MutationKey | MutationFilters, + arg2?: Omit | Options, + arg3?: Options, +): Accessor { + const [filters, options = {}] = parseMutationFilterArgs(arg1, arg2, arg3) + + const queryClient = useQueryClient({ context: options.context }) + const mutationCache = queryClient.getMutationCache() + + const [mutations, setMutations] = createSignal( + queryClient.isMutating(filters), + ) + + const unsubscribe = mutationCache.subscribe((_result) => { + setMutations(queryClient.isMutating(filters)) + }) + + onCleanup(() => { + unsubscribe() + }) + + return mutations +} diff --git a/packages/solid-query/src/utils.ts b/packages/solid-query/src/utils.ts new file mode 100644 index 0000000000..6e418e5911 --- /dev/null +++ b/packages/solid-query/src/utils.ts @@ -0,0 +1,85 @@ +import type { + SolidQueryKey, + SolidQueryFilters, + ParseFilterArgs, + ParseQueryArgs, +} from './types' +import type { QueryFunction, QueryOptions } from '@tanstack/query-core' + +export function isQueryKey(value: unknown): value is SolidQueryKey { + return typeof value === 'function' +} + +// The parseQuery Args functions helps normalize the arguments into the correct form. +// Whatever the parameters are, they are normalized into the correct form. +export function parseQueryArgs< + TOptions extends Omit< + QueryOptions>, + 'queryKey' + > & { + queryKey?: TQueryKey + }, + TQueryKey extends () => readonly unknown[] = SolidQueryKey, +>( + arg1: TQueryKey | TOptions, + arg2?: QueryFunction> | TOptions, + arg3?: TOptions, +): ParseQueryArgs { + if (!isQueryKey(arg1)) { + const { queryKey: solidKey, ...opts } = arg1 as any + if (solidKey) { + return { + ...opts, + queryKey: solidKey(), + } + } + return arg1 as any + } + + if (typeof arg2 === 'function') { + return { ...arg3, queryKey: arg1(), queryFn: arg2 } as any + } + + return { ...arg2, queryKey: arg1() } as any +} + +export function parseFilterArgs< + TFilters extends SolidQueryFilters, + TOptions = unknown, +>( + arg1?: SolidQueryKey | TFilters, + arg2?: TFilters | TOptions, + arg3?: TOptions, +): [ParseFilterArgs, TOptions | undefined] { + return ( + isQueryKey(arg1) + ? [{ ...arg2, queryKey: arg1() }, arg3] + : [{ ...arg1, queryKey: arg1?.queryKey?.() }, arg2] + ) as [ParseFilterArgs, TOptions] +} + +export function shouldThrowError boolean>( + _useErrorBoundary: boolean | T | undefined, + params: Parameters, +): boolean { + // Allow useErrorBoundary function to override throwing behavior on a per-error basis + if (typeof _useErrorBoundary === 'function') { + return _useErrorBoundary(...params) + } + + return !!_useErrorBoundary +} + +export function sleep(timeout: number): Promise { + return new Promise((resolve) => { + setTimeout(resolve, timeout) + }) +} + +/** + * Schedules a microtask. + * This can be useful to schedule state updates after rendering. + */ +export function scheduleMicrotask(callback: () => void) { + sleep(0).then(callback) +} diff --git a/packages/solid-query/tsconfig.json b/packages/solid-query/tsconfig.json new file mode 100644 index 0000000000..487fed1758 --- /dev/null +++ b/packages/solid-query/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "rootDir": "./src", + "outDir": "./build/solid", + "declarationDir": "./build/lib", + "tsBuildInfoFile": "./build/.tsbuildinfo", + "jsx": "preserve", + "jsxImportSource": "solid-js", + "emitDeclarationOnly": false + }, + "include": ["src"], + // comment this out to fix errors in tests during development + "exclude": ["src/__tests__"], + "references": [ + { "path": "../query-core" } + ] +} \ No newline at end of file diff --git a/packages/solid-query/tsconfig.lint.json b/packages/solid-query/tsconfig.lint.json new file mode 100644 index 0000000000..2db1490705 --- /dev/null +++ b/packages/solid-query/tsconfig.lint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "rootDir": "./src", + "outDir": "./build/solid", + "declarationDir": "./build/lib", + "tsBuildInfoFile": "./build/.tsbuildinfo", + "jsx": "preserve", + "jsxImportSource": "solid-js", + "emitDeclarationOnly": false + }, + "include": ["src"], + "references": [ + { "path": "../query-core" } + ] +} \ No newline at end of file diff --git a/rollup.config.ts b/rollup.config.ts index a3188d07b0..daf289dca5 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -141,6 +141,21 @@ export default function rollup(options: RollupOptions): RollupOptions[] { '@tanstack/react-query': 'ReactQuery', }, }), + ...buildConfigs({ + name: 'solid-query', + packageDir: 'packages/solid-query', + jsName: 'SolidQuery', + outputFile: 'index', + entryFile: 'src/index.ts', + globals: { + 'solid-js/store': 'SolidStore', + 'solid-js': 'Solid', + '@tanstack/query-core': 'QueryCore', + }, + bundleUMDGlobals: [ + '@tanstack/query-core', + ], + }), ] } diff --git a/scripts/config.ts b/scripts/config.ts index d51f2577a4..f3ec0d24bf 100644 --- a/scripts/config.ts +++ b/scripts/config.ts @@ -30,6 +30,11 @@ export const packages: Package[] = [ packageDir: 'react-query-persist-client', srcDir: 'src', }, + { + name: '@tanstack/solid-query', + packageDir: 'solid-query', + srcDir: 'src', + } ] export const latestBranch = 'main' diff --git a/tsconfig.base.json b/tsconfig.base.json index c3524d1386..c8845b11ec 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -32,7 +32,8 @@ "@tanstack/react-query-devtools": ["packages/react-query-devtools/src"], "@tanstack/react-query-persist-client": [ "packages/react-query-persist-client/src" - ] + ], + "@tanstack/solid-query": ["packages/solid-query/src"], } } } diff --git a/tsconfig.json b/tsconfig.json index c397b08ace..a6f9a8a2b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ { "path": "packages/query-sync-storage-persister" }, { "path": "packages/react-query" }, { "path": "packages/react-query-devtools" }, - { "path": "packages/react-query-persist-client" } + { "path": "packages/react-query-persist-client" }, + { "path": "packages/solid-query" }, ] }