[
  {
    "path": ".browserslistrc",
    "content": "# Browsers we support\nChrome >= 73\nFirefox >= 78\nEdge >= 79\nSafari >= 12.0\niOS >= 12.0\nopera >= 53\n"
  },
  {
    "path": ".commitlintrc.json",
    "content": "{\n  \"extends\": [\"@commitlint/config-conventional\"]\n}\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"shared-node-browser\": true,\n    \"node\": true,\n    \"es6\": true\n  },\n  \"extends\": [\n    \"eslint:recommended\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"prettier\",\n    \"plugin:prettier/recommended\",\n    \"plugin:react-hooks/recommended\",\n    \"plugin:import/errors\",\n    \"plugin:import/warnings\"\n  ],\n  \"plugins\": [\n    \"@typescript-eslint\",\n    \"react\",\n    \"prettier\",\n    \"react-hooks\",\n    \"import\",\n    \"jest\"\n  ],\n  \"parser\": \"@typescript-eslint/parser\",\n  \"parserOptions\": {\n    \"project\": 2018,\n    \"sourceType\": \"module\",\n    \"ecmaFeatures\": {\n      \"jsx\": true\n    }\n  },\n  \"rules\": {\n    \"eqeqeq\": \"error\",\n    \"no-var\": \"error\",\n    \"prefer-const\": \"error\",\n    \"curly\": [\"warn\", \"multi-line\", \"consistent\"],\n    \"no-console\": \"off\",\n    \"@typescript-eslint/no-non-null-assertion\": \"off\",\n    \"import/no-unresolved\": [\"error\", { \"commonjs\": true, \"amd\": true }],\n    \"import/export\": \"error\",\n    \"@typescript-eslint/ban-types\": \"off\",\n    \"@typescript-eslint/no-duplicate-imports\": [\"error\"],\n    \"@typescript-eslint/explicit-module-boundary-types\": \"off\",\n    \"@typescript-eslint/no-unused-vars\": [\n      \"warn\",\n      { \"argsIgnorePattern\": \"^_\", \"varsIgnorePattern\": \"^_\" }\n    ],\n    \"@typescript-eslint/no-use-before-define\": \"off\",\n    \"@typescript-eslint/no-empty-function\": \"off\",\n    \"@typescript-eslint/no-explicit-any\": \"off\",\n    \"@typescript-eslint/ban-ts-comment\": \"off\",\n    \"jest/consistent-test-it\": [\n      \"error\",\n      { \"fn\": \"it\", \"withinDescribe\": \"it\" }\n    ],\n    \"import/order\": \"off\",\n    \"react/jsx-uses-react\": \"off\",\n    \"react/react-in-jsx-scope\": \"off\",\n    \"sort-imports\": [\n      \"error\",\n      {\n        \"ignoreDeclarationSort\": true\n      }\n    ]\n  },\n  \"settings\": {\n    \"react\": {\n      \"version\": \"detect\"\n    },\n    \"import/extensions\": [\".js\", \".jsx\", \".ts\", \".tsx\"],\n    \"import/parsers\": {\n      \"@typescript-eslint/parser\": [\".js\", \".jsx\", \".ts\", \".tsx\"]\n    },\n    \"import/resolver\": {\n      \"node\": {\n        \"extensions\": [\".js\", \".jsx\", \".ts\", \".tsx\", \".json\"],\n        \"paths\": [\"src\"]\n      }\n    }\n  },\n  \"overrides\": [\n    {\n      \"files\": [\"src\"],\n      \"parserOptions\": {\n        \"project\": \"./tsconfig.json\"\n      }\n    },\n    {\n      \"files\": [\"tests/**/*.tsx\"],\n      \"env\": {\n        \"jest/globals\": true\n      }\n    },\n    {\n      \"files\": [\"./*.js\"],\n      \"rules\": {\n        \"@typescript-eslint/no-var-requires\": \"off\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: 'publish'\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  release:\n    name: publish\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-node@v3\n        with:\n          node-version-file: .nvmrc\n          registry-url: https://registry.npmjs.org\n          cache: 'yarn'\n      - run: |\n          git config --global user.name 'liaoliao666'\n          git config --global user.email '1076988944@qq.com'\n          yarn\n          yarn test && yarn build\n      - uses: JS-DevTools/npm-publish@v1\n        with:\n          token: ${{ secrets.NPM_AUTH_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\n\non:\n  push:\n    branches:\n      - main\njobs:\n  tests:\n    name: Building package\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v3\n\n      - name: Caching node_modules\n        uses: actions/cache@v3\n        id: yarn-cache\n        with:\n          path: \"**/node_modules\"\n          key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}\n          restore-keys: |\n            ${{ runner.os }}-node-\n      - name: Setup Node\n        uses: actions/setup-node@v3\n        with:\n          node-version-file: '.nvmrc'\n          cache: 'yarn'\n\n      - name: Install dependencies 🔧\n        if: steps.yarn-cache.outputs.cache-hit != 'true'\n        run: yarn install --frozen-lockfile\n\n      - name: Test package 🔧\n        run: yarn test\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\n\n# builds\nbuild\n\n# misc\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nyarn.lock\npackage-lock.json\nsize-plugin.json\nstats.json\nstats.html\n\n# mac\n.DS_Store"
  },
  {
    "path": ".husky/commit-msg",
    "content": "#!/usr/bin/env sh\n. \"$(dirname -- \"$0\")/_/husky.sh\"\n\nnpx commitlint --edit \n"
  },
  {
    "path": ".npmrc",
    "content": "auto-install-peers=true\nregistry=https://registry.npmjs.org"
  },
  {
    "path": ".nvmrc",
    "content": "v16.19.0"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 liaoliao666\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README-zh_CN.md",
    "content": "<div align=\"center\">\n\n<br />\n<br />\n\n<p align=\"center\">\n  <a aria-label=\"NPM version\" href=\"./assets/logo.svg\">\n    <img alt=\"\" src=\"./assets/logo.svg\" height=\"40\">\n  </a>\n</p>\n\n<p>🕊️ 一个用于 ReactQuery 的工具包，它能使 ReactQuery 更易复用和类型安全</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/liaoliao666/react-query-kit/actions/workflows/tests.yml\"><img src=\"https://github.com/liaoliao666/react-query-kit/actions/workflows/tests.yml/badge.svg?branch=main\" alt=\"Latest build\" target=\"\\_parent\"></a>\n  <a href=\"https://www.npmjs.com/package/react-query-kit\"><img src=\"https://badgen.net/npm/v/react-query-kit\" alt=\"Latest published version\" target=\"\\_parent\"></a>\n  <a href=\"https://github.com/liaoliao666/react-query-kit\"><img src=\"https://badgen.net/npm/types/react-query-kit\" alt=\"Types included\" target=\"\\_parent\"></a>\n  <a href=\"https://www.npmjs.com/package/react-query-kit\"><img src=\"https://badgen.net/npm/license/react-query-kit\" alt=\"License\" target=\"\\_parent\"></a>\n  <a href=\"https://www.npmjs.com/package/react-query-kit\"><img src=\"https://badgen.net/npm/dt/react-query-kit\" alt=\"Number of downloads\" target=\"\\_parent\"></a>\n  <a href=\"https://github.com/liaoliao666/react-query-kit\"><img src=\"https://img.shields.io/github/stars/liaoliao666/react-query-kit.svg?style=social&amp;label=Star\" alt=\"GitHub Stars\" target=\"\\_parent\"></a>\n</p>\n</div>\n\n---\n\n## Motivation\n\n- 以类型安全的方式管理 `queryKey`\n- 让 `queryClient` 的操作更清楚地关联到哪个自定义 hook\n- 可以从任何自定义 ReactQuery hook 中提取的 TypeScript 类型\n- 中间件\n\n[English](./README.md) | 简体中文\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [安装](#installation)\n- [例子](#examples)\n- 使用\n  - [createQuery](#createquery)\n  - [createInfiniteQuery](#createinfinitequery)\n  - [createSuspenseQuery](#createsuspensequery)\n  - [createSuspenseInfiniteQuery](#createsuspenseinfinitequery)\n  - [createMutation](#createmutation)\n  - [router](#router)\n  - [中间件](#中间件)\n  - [TypeScript](#typescript)\n  - [类型推导](#类型推导)\n  - [禁用查询](#禁用查询)\n- [常见问题](#常见问题)\n- [迁移](#迁移)\n- [Issues](#issues)\n  - [🐛 Bugs](#-bugs)\n  - [💡 Feature Requests](#-feature-requests)\n- [LICENSE](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\n```bash\n$ npm i react-query-kit\n# or\n$ yarn add react-query-kit\n```\n\n如果您还在使用 React Query Kit v2？ 请在此处查看 v2 文档：https://github.com/liaoliao666/react-query-kit/tree/v2#readme.\n\n# Examples\n\n- [Basic](https://codesandbox.io/s/example-react-query-kit-basic-1ny2j8)\n- [Optimistic Updates](https://codesandbox.io/s/example-react-query-kit-optimistic-updates-eefg0v)\n- [Next.js](https://codesandbox.io/s/example-react-query-kit-nextjs-uldl88)\n- [Load-More & Infinite Scroll](https://codesandbox.io/s/example-react-query-kit-load-more-infinite-scroll-vg494v)\n\n## createQuery\n\n### Usage\n\n```tsx\nimport { QueryClient, dehydrate } from '@tanstack/react-query'\nimport { createQuery } from 'react-query-kit'\n\ntype Data = { title: string; content: string }\ntype Variables = { id: number }\n\nconst usePost = createQuery({\n  queryKey: ['posts'],\n  fetcher: (variables: Variables): Promise<Data> => {\n    return fetch(`/posts/${variables.id}`).then(res => res.json())\n  },\n  // 你还可以通过中间件来定制这个 hook 的行为\n  use: [myMiddleware]\n})\n\nconst variables = { id: 1 }\n\n// example\nexport default function Page() {\n  // queryKey 相等于 ['/posts', { id: 1 }]\n  const { data } = usePost({ variables })\n\n  return (\n    <div>\n      <div>{data?.title}</div>\n      <div>{data?.content}</div>\n    </div>\n  )\n}\n\nconsole.log(usePost.getKey()) //  ['/posts']\nconsole.log(usePost.getKey(variables)) //  ['/posts', { id: 1 }]\n\n// nextjs 例子\nexport async function getStaticProps() {\n  const queryClient = new QueryClient()\n\n  await queryClient.prefetchQuery(usePost.getFetchOptions(variables))\n\n  return {\n    props: {\n      dehydratedState: dehydrate(queryClient),\n    },\n  }\n}\n\n// 在 react 组件外使用\nconst data = await queryClient.fetchQuery(\n  usePost.getFetchOptions(variables)\n)\n\n// useQueries 例子\nconst queries = useQueries({\n  queries: [\n    usePost.getOptions(variables)，\n    useUser.getOptions(),\n  ],\n})\n\n// getQueryData\nqueryClient.getQueryData(usePost.getKey(variables)) // Data\n\n// setQueryData\nqueryClient.setQueryData(usePost.getKey(variables), {...})\n```\n\n### 额外的 API 文档\n\nOptions\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n  - 必填\n  - 用于请求数据的函数。 第二个参数是“queryFn”的“QueryFunctionContext”\n- `variables?: TVariables`\n  - 可选\n  - `variables` 将是 fetcher 的第一个参数和 `queryKey` 数组的最后一个元素\n- `use: Middleware[]`\n  - 可选\n  - 中间件函数数组 [(详情)](#中间件)\n\nExpose Methods\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n- `getKey: (variables: TVariables) => QueryKey`\n- `getOptions: (variables: TVariables) => UseInfiniteQueryOptions`\n- `getFetchOptions: (variables: TVariables) => ({ queryKey, queryFn, queryKeyHashFn })`\n\n## createInfiniteQuery\n\n### Usage\n\n```tsx\nimport { QueryClient, dehydrate } from '@tanstack/react-query'\nimport { createInfiniteQuery } from 'react-query-kit'\n\ntype Data = { projects: { id: string; name: string }[]; nextCursor: number }\ntype Variables = { active: boolean }\n\nconst useProjects = createInfiniteQuery({\n  queryKey: ['projects'],\n  fetcher: (variables: Variables, { pageParam }): Promise<Data> => {\n    return fetch(\n      `/projects?cursor=${pageParam}?active=${variables.active}`\n    ).then(res => res.json())\n  },\n  getNextPageParam: (lastPage, pages) => lastPage.nextCursor,\n  initialPageParam: 0,\n})\n\nconst variables = { active: true }\n\n// example\nexport default function Page() {\n  // queryKey equals to ['projects', { active: true }]\n  const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =\n    useProjects({ variables })\n\n  return (\n    <div>\n      {data.pages.map((group, i) => (\n        <React.Fragment key={i}>\n          {group.projects.map(project => (\n            <p key={project.id}>{project.name}</p>\n          ))}\n        </React.Fragment>\n      ))}\n      <div>\n        <button\n          onClick={() => fetchNextPage()}\n          disabled={!hasNextPage || isFetchingNextPage}\n        >\n          {isFetchingNextPage\n            ? 'Loading more...'\n            : hasNextPage\n            ? 'Load More'\n            : 'Nothing more to load'}\n        </button>\n      </div>\n      <div>{isFetching && !isFetchingNextPage ? 'Fetching...' : null}</div>\n    </div>\n  )\n}\n\n// nextjs example\nexport async function getStaticProps() {\n  const queryClient = new QueryClient()\n\n  await queryClient.prefetchInfiniteQuery(\n    useProjects.getFetchOptions(variables)\n  )\n\n  return {\n    props: {\n      dehydratedState: dehydrate(queryClient),\n    },\n  }\n}\n\n// 在 react 组件外使用\nconst data = await queryClient.fetchInfiniteQuery(\n  useProjects.getFetchOptions(variables)\n)\n```\n\n### 额外的 API 文档\n\nOptions\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n  - 必填\n  - 查询将用于请求数据的函数。 第二个参数是“queryFn”的“QueryFunctionContext”\n- `variables?: TVariables`\n  - 可选\n  - `variables` 将是 fetcher 的第一个参数和 `queryKey` 数组的最后一个元素\n- `use: Middleware[]`\n  - 可选\n  - 中间件函数数组 [(详情)](#中间件)\n\nExpose Methods\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n- `getKey: (variables: TVariables) => QueryKey`\n- `getOptions: (variables: TVariables) => UseInfiniteQueryOptions`\n- `getFetchOptions: (variables: TVariables) => ({ queryKey, queryFn, queryKeyHashFn, getNextPageParam, getPreviousPageParam, initialPageParam })`\n\n## createSuspenseQuery\n\n这与在查询配置中将 suspense 选项设置为 true 具有相同的效果，但在 TypeScript 的体验更好，因为 data 是有定义的（因为错误和加载状态由 Suspense 和 ErrorBoundaries 处理）。\n\n```ts\nimport { createSuspenseQuery } from 'react-query-kit'\n\ncreateSuspenseQuery({\n  ...options,\n})\n\n// 相当于\ncreateQuery({\n  ...options,\n  enabled: true,\n  suspense: true,\n  throwOnError: true,\n})\n```\n\n## createSuspenseInfiniteQuery\n\n```ts\nimport { createSuspenseInfiniteQuery } from 'react-query-kit'\n\ncreateSuspenseInfiniteQuery({\n  ...options,\n})\n\n// 相当于\ncreateInfiniteQuery({\n  ...options,\n  enabled: true,\n  suspense: true,\n  throwOnError: true,\n})\n```\n\n## createMutation\n\n### Usage\n\n```tsx\nimport { createMutation } from 'react-query-kit'\n\nconst useAddTodo = createMutation(\n  async (variables: { title: string; content: string }) =>\n    fetch('/post', {\n      method: 'POST',\n      body: JSON.stringify(variables),\n    }).then(res => res.json()),\n  {\n    onSuccess(data, variables, context) {\n      // do somethings\n    },\n  }\n)\n\nfunction App() {\n  const mutation = useAddTodo({\n    onSettled: (data, error, variables, context) => {\n      // Error or success... doesn't matter!\n    },\n  })\n\n  return (\n    <div>\n      {mutation.isPending ? (\n        'Adding todo...'\n      ) : (\n        <>\n          {mutation.isError ? (\n            <div>An error occurred: {mutation.error.message}</div>\n          ) : null}\n\n          {mutation.isSuccess ? <div>Todo added!</div> : null}\n\n          <button\n            onClick={() => {\n              mutation.mutate({ title: 'Do Laundry', content: 'content...' })\n            }}\n          >\n            Create Todo\n          </button>\n        </>\n      )}\n    </div>\n  )\n}\n\n// usage outside of react component\nuseAddTodo.mutationFn({ title: 'Do Laundry', content: 'content...' })\n```\n\n### 额外的 API 文档\n\nOptions\n\n- `use: Middleware[]`\n  - 可选\n  - 中间件函数数组 [(详情)](#中间件)\n\nExpose Methods\n\n- `getKey: () => MutationKey`\n- `getOptions: () => UseMutationOptions`\n- `mutationFn: ExposeMutationFn<TData, TVariables>`\n\n## router\n\n`router` 允许您创建整个 API 的形状\n\n### Usage\n\n```tsx\nimport { router } from 'react-query-kit'\n\nconst post = router(`post`, {\n  byId: router.query({\n    fetcher: (variables: { id: number }) =>\n      fetch(`/posts/${variables.id}`).then(res => res.json()),\n    use: [myMiddleware],\n  }),\n\n  list: router.infiniteQuery({\n    fetcher: (_variables, { pageParam }) =>\n      fetch(`/posts/?cursor=${pageParam}`).then(res => res.json()),\n    getNextPageParam: lastPage => lastPage.nextCursor,\n    initialPageParam: 0,\n  }),\n\n  add: router.mutation({\n    mutationFn: async (variables: { title: string; content: string }) =>\n      fetch('/posts', {\n        method: 'POST',\n        body: JSON.stringify(variables),\n      }).then(res => res.json()),\n  }),\n\n  // nest router\n  command: {\n    report: router.mutation({ mutationFn }),\n\n    promote: router.mutation({ mutationFn }),\n  },\n})\n\n// get root key\npost.getKey() // ['post']\n\n// hooks\npost.byId.useQuery({ variables: { id: 1 } })\npost.byId.useSuspenseQuery({ variables: { id: 1 } })\npost.list.useInfiniteQuery()\npost.list.useSuspenseInfiniteQuery()\npost.add.useMutation()\npost.command.report.useMutation()\n\n// expose methods\npost.byId.getKey({ id: 1 }) // ['post', 'byId', { id: 1 }]\npost.byId.getFetchOptions({ id: 1 })\npost.byId.getOptions({ id: 1 })\npost.byId.fetcher({ id: 1 })\npost.add.getKey() // ['post', 'add']\npost.add.getOptions()\npost.add.mutationFn({ title: 'title', content: 'content' })\n\n// infer types\ntype Data = inferData<typeof post.list>\ntype FnData = inferFnData<typeof post.list>\ntype Variables = inferVariables<typeof post.list>\ntype Error = inferError<typeof post.list>\n```\n\n### 合并路由\n\n```ts\nimport { router } from 'react-query-kit'\n\nconst user = router(`user`, {})\nconst post = router(`post`, {})\n\nconst k = {\n  user,\n  post,\n}\n```\n\n### API 文档\n\n`type Router = (key: string | unknown[], config: TConfig) => TRouter`\n\nExpose Methods\n\n- `query`\n  与 `createQuery` 类似，但无需选项 `queryKey`\n- `infiniteQuery`\n  与 `createInfiniteQuery` 类似，但无需选项 `queryKey`\n- `mutation`\n  与 `createMutation` 类似，但无需选项 `mutationKey`\n\n## 中间件\n\n此功能的灵感来自于 [SWR 的中间件功能](https://swr.vercel.app/docs/middleware)。\n\n中间件接收 hook，可以在运行它之前和之后执行逻辑。如果有多个中间件，则每个中间件包装下一个中间件。列表中的最后一个中间件将接收原始的 hook。\n\n### 使用\n\n```ts\nimport { QueryClient } from '@tanstack/react-query'\nimport { Middleware, MutationHook, QueryHook, getKey } from 'react-query-kit'\n\nconst logger: Middleware<QueryHook<Data, Variables>> = useQueryNext => {\n  return options => {\n    const log = useLogger()\n    const fetcher = (variables, context) => {\n      log(context.queryKey, variables)\n      return options.fetcher(variables, context)\n    }\n\n    return useQueryNext({\n      ...options,\n      fetcher,\n    })\n  }\n}\n\nconst useUser = createQuery<Data, Variables>({\n  use: [logger],\n})\n\n// 全局中间件\nconst queryMiddleware: Middleware<QueryHook> = useQueryNext => {\n  return options => {\n    // 你还可以通过函数 getKey 获取 queryKey\n    const fullKey = getKey(options.queryKey, options.variables)\n    // ...\n    return useQueryNext(options)\n  }\n}\nconst mutationMiddleware: Middleware<MutationHook> = useMutationNext => {\n  return options => {\n    // ...\n    return useMutationNext(options)\n  }\n}\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      use: [queryMiddleware],\n    },\n    mutations: {\n      use: [mutationMiddleware],\n    },\n  },\n})\n```\n\n### 扩展\n\n中间件将从上级合并。例如：\n\n```jsx\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      use: [a],\n    },\n  },\n})\n\nconst useSomething = createQuery({\n  use: [b],\n})\n\nuseSomething({ use: [c] })\n```\n\n相当于：\n\n```js\ncreateQuery({ use: [a, b, c] })\n```\n\n### 多个中间件\n\n每个中间件包装下一个中间件，最后一个只包装 useQuery hook。例如：\n\n```jsx\ncreateQuery({ use: [a, b, c] })\n```\n\n中间件执行的顺序是 `a → b → c`，如下所示：\n\n```plaintext\nenter a\n  enter b\n    enter c\n      useQuery()\n    exit  c\n  exit  b\nexit  a\n```\n\n### 多个 QueryClient\n\n在 ReactQuery v5 中，`QueryClient` 将是 `useQuery` 和 `useMutation` 的第二个参数。 如果你在全局中有多个 `QueryClient`，你应该在中间件钩子中接收 `QueryClient`\n\n```ts\nconst useSomething = createQuery({\n  use: [\n    function myMiddleware(useQueryNext) {\n      // 你应该接收 queryClient 作为第二个参数\n      return (options, queryClient) => {\n        const client = useQueryClient(queryClient)\n        // ...\n        return useQueryNext(options, queryClient)\n      }\n    },\n  ],\n})\n\n// 如果你传入另一个 QueryClient\nuseSomething({...}, anotherQueryClient)\n```\n\n## TypeScript\n\n默认情况下，ReactQueryKit 还会从 `fetcher` 推断 `data` 和 `variables` 的类型，因此您可以自动获得首选类型。\n\n```ts\ntype Data = { title: string; content: string }\ntype Variables = { id: number }\n\nconst usePost = createQuery({\n  queryKey: ['posts'],\n  fetcher: (variables: Variables): Promise<Data> => {\n    return fetch(`/posts/${variables}`).then(res => res.json())\n  },\n})\n\n// `data` 将被推断为 `Data | undefined`.\n// `variables` 将被推断为 `Variables`.\nconst { data } = usePost({ variables: { id: 1 } })\n```\n\n您还可以显式指定 `fetcher` 参数和返回的类型。\n\n```ts\ntype Data = { title: string; content: string }\ntype Variables = { id: number }\n\nconst usePost = createQuery<Data, Variables, Error>({\n  queryKey: ['posts'],\n  fetcher: variables => {\n    return fetch(`/posts/${variables}`).then(res => res.json())\n  },\n})\n\n// `data` 将被推断为 `Data | undefined`.\n// `error` 将被推断为 `Error | null`\n// `variables` 将被推断为 `Variables`.\nconst { data, error } = usePost({ variables: { id: 1 } })\n```\n\n## 类型推导\n\n您可以使用 `inferData` 或 `inferVariables` 提取任何自定义 hook 的 TypeScript 类型\n\n```ts\nimport { inferData, inferFnData, inferError, inferVariables, inferOptions } from 'react-query-kit'\n\nconst useProjects = createInfiniteQuery<Data, Variables>(...)\n\ninferData<typeof useProjects> // InfiniteData<Data>\ninferFnData<typeof useProjects> // Data\ninferVariables<typeof useProjects> // Variables\ninferError<typeof useProjects> // Error\ninferOptions<typeof useProjects> // InfiniteQueryHookOptions<...>\n```\n\n## 禁用查询\n\n要禁用查询，您可以将 `skipToken` 作为选项 `variables` 传递给您的自定义查询。这将阻止查询被执行。\n\n```ts\nimport { skipToken } from '@tanstack/react-query'\n\nconst [name, setName] = useState<string | undefined>()\nconst result = usePost({\n  variables: id ? { id: id } : skipToken,\n})\n\n// 以及用于 useQueries 的示例\nconst queries = useQueries({\n  queries: [usePost.getOptions(id ? { id: id } : skipToken)],\n})\n```\n\n## 常见问题\n\n### `getFetchOptions` 和 `getOptions` 有什么不同\n\n`getFetchOptions` 只会返回必要的选项，而像 `staleTime` 和 `retry` 等选项会被忽略\n\n### `fetcher` 和 `queryFn` 有什么不同\n\nReactQueryKit 会自动将 `fetcher` 转换为 `queryFn`，例如\n\n```ts\nconst useTest = createQuery({\n  queryKey: ['test'],\n  fetcher: (variables, context) => {\n    // ...\n  },\n})\n\n// => useTest.getOptions(variables):\n// {\n//   queryKey: ['test', variables],\n//   queryFn: (context) => fetcher(variables, context)\n// }\n```\n\n## 迁移\n\n从 ReactQueryKit 2 升级 → ReactQueryKit 3\n\n```diff\ncreateQuery({\n-  primaryKey: 'posts',\n-  queryFn: ({ queryKey: [_primaryKey, variables] }) => {},\n+  queryKey: ['posts'],\n+  fetcher: variables => {},\n})\n```\n\n您可以从 ReactQueryKit 3 中受益\n\n- 支持传入数组 `queryKey`\n- 支持推断 fetcher 的类型，您可以自动享受首选的类型。\n- 支持创建整个 API 的形状\n\n## Issues\n\n_Looking to contribute? Look for the [Good First Issue][good-first-issue]\nlabel._\n\n### 🐛 Bugs\n\n请针对错误、缺少文档或意外行为提出问题。\n\n[**See Bugs**][bugs]\n\n### 💡 Feature Requests\n\n请提交问题以建议新功能。 通过添加对功能请求进行投票\n一个 👍。 这有助于维护人员优先处理要处理的内容。\n\n[**See Feature Requests**][requests]\n\n## LICENSE\n\nMIT\n\n<!-- prettier-ignore-start -->\n[npm]: https://www.npmjs.com\n[node]: https://nodejs.org\n[bugs]: https://github.com/liaoliao666/react-query-kit/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug\n[requests]: https://github.com/liaoliao666/react-query-kit/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement\n[good-first-issue]: https://github.com/liaoliao666/react-query-kit/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement+label%3A%22good+first+issue%22\n<!-- prettier-ignore-end -->\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n<br />\n<br />\n\n<p align=\"center\">\n  <a aria-label=\"NPM version\" href=\"./assets/logo.svg\">\n    <img alt=\"\" src=\"./assets/logo.svg\" height=\"40\">\n  </a>\n</p>\n\n<p>🕊️ A toolkit for ReactQuery that make ReactQuery reusable and typesafe</p>\n\n<p align=\"center\">\n  <a href=\"https://github.com/liaoliao666/react-query-kit/actions/workflows/tests.yml\"><img src=\"https://github.com/liaoliao666/react-query-kit/actions/workflows/tests.yml/badge.svg?branch=main\" alt=\"Latest build\" target=\"\\_parent\"></a>\n  <a href=\"https://www.npmjs.com/package/react-query-kit\"><img src=\"https://badgen.net/npm/v/react-query-kit\" alt=\"Latest published version\" target=\"\\_parent\"></a>\n  <a href=\"https://github.com/liaoliao666/react-query-kit\"><img src=\"https://badgen.net/npm/types/react-query-kit\" alt=\"Types included\" target=\"\\_parent\"></a>\n  <a href=\"https://github.com/liaoliao666/react-query-kit/blob/main/LICENSE\"><img src=\"https://badgen.net/npm/license/react-query-kit\" alt=\"License\" target=\"\\_parent\"></a>\n  <a href=\"https://www.npmjs.com/package/react-query-kit\"><img src=\"https://badgen.net/npm/dt/react-query-kit\" alt=\"Number of downloads\" target=\"\\_parent\"></a>\n  <a href=\"https://github.com/liaoliao666/react-query-kit\"><img src=\"https://img.shields.io/github/stars/liaoliao666/react-query-kit.svg?style=social&amp;label=Star\" alt=\"GitHub Stars\" target=\"\\_parent\"></a>\n</p>\n</div>\n\n---\n\n## What could you benefit from it\n\n- Manage `queryKey` in a type-safe way\n- Make `queryClient`'s operations clearly associated with custom ReactQuery hooks\n- You can extract the TypeScript type of any custom ReactQuery hooks\n- Middleware\n\nEnglish | [简体中文](./README-zh_CN.md)\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n- [Examples](#examples)\n- Usage\n  - [createQuery](#createquery)\n  - [createInfiniteQuery](#createinfinitequery)\n  - [createSuspenseQuery](#createsuspensequery)\n  - [createSuspenseInfiniteQuery](#createsuspenseinfinitequery)\n  - [createMutation](#createmutation)\n  - [router](#router)\n  - [Middleware](#middleware)\n  - [TypeScript](#typescript)\n  - [Type inference](#type-inference)\n  - [Disabling Queries](#disabling-queries)\n- [FAQ](#faq)\n- [Migration](#migration)\n- [Issues](#issues)\n  - [🐛 Bugs](#-bugs)\n  - [💡 Feature Requests](#-feature-requests)\n- [LICENSE](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nThis module is distributed via [npm][npm] which is bundled with [node][node] and\nshould be installed as one of your project's `dependencies`:\n\n```bash\n$ npm i react-query-kit\n# or\n$ yarn add react-query-kit\n```\n\nIf you still on React Query Kit v2? Check out the v2 docs here: https://github.com/liaoliao666/react-query-kit/tree/v2#readme.\n\n# Examples\n\n- [Basic](https://codesandbox.io/s/example-react-query-kit-basic-1ny2j8)\n- [Optimistic Updates](https://codesandbox.io/s/example-react-query-kit-optimistic-updates-eefg0v)\n- [Next.js](https://codesandbox.io/s/example-react-query-kit-nextjs-uldl88)\n- [Load-More & Infinite Scroll](https://codesandbox.io/s/example-react-query-kit-load-more-infinite-scroll-vg494v)\n\n## createQuery\n\n### Usage\n\n```tsx\nimport { QueryClient, dehydrate } from '@tanstack/react-query'\nimport { createQuery } from 'react-query-kit'\n\ntype Data = { title: string; content: string }\ntype Variables = { id: number }\n\nconst usePost = createQuery({\n  queryKey: ['posts'],\n  fetcher: (variables: Variables): Promise<Data> => {\n    return fetch(`/posts/${variables.id}`).then(res => res.json())\n  },\n  // u can also pass middleware to cutomize this hook's behavior\n  use: [myMiddleware]\n})\n\n\nconst variables = { id: 1 }\n\n// example\nexport default function Page() {\n  // queryKey will be `['posts', { id: 1 }]` if u passed variables\n  const { data } = usePost({ variables })\n\n  return (\n    <div>\n      <div>{data?.title}</div>\n      <div>{data?.content}</div>\n    </div>\n  )\n}\n\nconsole.log(usePost.getKey()) //  ['posts']\nconsole.log(usePost.getKey(variables)) //  ['posts', { id: 1 }]\n\n// nextjs example\nexport async function getStaticProps() {\n  const queryClient = new QueryClient()\n\n  await queryClient.prefetchQuery(usePost.getFetchOptions(variables))\n\n  return {\n    props: {\n      dehydratedState: dehydrate(queryClient),\n    },\n  }\n}\n\n// usage outside of react component\nconst data = await queryClient.fetchQuery(usePost.getFetchOptions(variables))\n\n// useQueries example\nconst queries = useQueries({\n  queries: [\n   usePost.getOptions(variables),\n   useUser.getOptions(),\n  ],\n})\n\n// getQueryData\nqueryClient.getQueryData(usePost.getKey(variables)) // Data\n\n// setQueryData\nqueryClient.setQueryData(usePost.getKey(variables), {...})\n```\n\n### Additional API Reference\n\nOptions\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n  - Required\n  - The function that the query will use to request data. And The second param is the `QueryFunctionContext` of `queryFn`.\n- `variables?: TVariables`\n  - Optional\n  - `variables` will be the frist param of fetcher and the last element of the `queryKey` array\n- `use: Middleware[]`\n  - Optional\n  - array of middleware functions [(details)](#middleware)\n\nExpose Methods\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n- `getKey: (variables: TVariables) => QueryKey`\n- `getOptions: (variables: TVariables) => UseQueryOptions`\n- `getFetchOptions: (variables: TVariables) => ({ queryKey, queryFn, queryKeyHashFn })`\n\n## createInfiniteQuery\n\n### Usage\n\n```tsx\nimport { QueryClient, dehydrate } from '@tanstack/react-query'\nimport { createInfiniteQuery } from 'react-query-kit'\n\ntype Data = { projects: { id: string; name: string }[]; nextCursor: number }\ntype Variables = { active: boolean }\n\nconst useProjects = createInfiniteQuery({\n  queryKey: ['projects'],\n  fetcher: (variables: Variables, { pageParam }): Promise<Data> => {\n    return fetch(\n      `/projects?cursor=${pageParam}?active=${variables.active}`\n    ).then(res => res.json())\n  },\n  getNextPageParam: (lastPage, pages) => lastPage.nextCursor,\n  initialPageParam: 0,\n})\n\nconst variables = { active: true }\n\n// example\nexport default function Page() {\n  // queryKey equals to ['projects', { active: true }]\n  const { data, fetchNextPage, hasNextPage, isFetching, isFetchingNextPage } =\n    useProjects({ variables })\n\n  return (\n    <div>\n      {data.pages.map((group, i) => (\n        <React.Fragment key={i}>\n          {group.projects.map(project => (\n            <p key={project.id}>{project.name}</p>\n          ))}\n        </React.Fragment>\n      ))}\n      <div>\n        <button\n          onClick={() => fetchNextPage()}\n          disabled={!hasNextPage || isFetchingNextPage}\n        >\n          {isFetchingNextPage\n            ? 'Loading more...'\n            : hasNextPage\n            ? 'Load More'\n            : 'Nothing more to load'}\n        </button>\n      </div>\n      <div>{isFetching && !isFetchingNextPage ? 'Fetching...' : null}</div>\n    </div>\n  )\n}\n\n// nextjs example\nexport async function getStaticProps() {\n  const queryClient = new QueryClient()\n\n  await queryClient.prefetchInfiniteQuery(\n    useProjects.getFetchOptions(variables)\n  )\n\n  return {\n    props: {\n      dehydratedState: dehydrate(queryClient),\n    },\n  }\n}\n\n// usage outside of react component\nconst data = await queryClient.fetchInfiniteQuery(\n  useProjects.getFetchOptions(variables)\n)\n```\n\n### Additional API Reference\n\nOptions\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n  - Required\n  - The function that the query will use to request data. And The second param is the `QueryFunctionContext` of `queryFn`.\n- `variables?: TVariables`\n  - Optional\n  - `variables` will be the frist param of fetcher and the last element of the `queryKey` array\n- `use: Middleware[]`\n  - Optional\n  - array of middleware functions [(details)](#middleware)\n\nExpose Methods\n\n- `fetcher: (variables: TVariables, context: QueryFunctionContext<QueryKey, TPageParam>) => TFnData | Promise<TFnData>`\n- `getKey: (variables: TVariables) => QueryKey`\n- `getOptions: (variables: TVariables) => UseInfiniteQueryOptions`\n- `getFetchOptions: (variables: TVariables) => ({ queryKey, queryFn, queryKeyHashFn, getNextPageParam, getPreviousPageParam, initialPageParam })`\n\n## createSuspenseQuery\n\nThis has the same effect as setting the `suspense` option to `true` in the query config, but it works better in TypeScript, because `data` is guaranteed to be defined (as errors and loading states are handled by Suspense- and ErrorBoundaries).\n\n```ts\nimport { createSuspenseQuery } from 'react-query-kit'\n\ncreateSuspenseQuery({\n  ...options,\n})\n\n// equals to\ncreateQuery({\n  ...options,\n  enabled: true,\n  suspense: true,\n  throwOnError: true,\n})\n```\n\n## createSuspenseInfiniteQuery\n\n```ts\nimport { createSuspenseInfiniteQuery } from 'react-query-kit'\n\ncreateSuspenseInfiniteQuery({\n  ...options,\n})\n\n// equals to\ncreateInfiniteQuery({\n  ...options,\n  enabled: true,\n  suspense: true,\n  throwOnError: true,\n})\n```\n\n## createMutation\n\n### Usage\n\n```tsx\nimport { createMutation } from 'react-query-kit'\n\nconst useAddTodo = createMutation({\n  mutationFn: async (variables: { title: string; content: string }) =>\n    fetch('/post', {\n      method: 'POST',\n      body: JSON.stringify(variables),\n    }).then(res => res.json()),\n  onSuccess(data, variables, context) {\n    // do somethings\n  },\n})\n\nfunction App() {\n  const mutation = useAddTodo({\n    onSettled: (data, error, variables, context) => {\n      // Error or success... doesn't matter!\n    },\n  })\n\n  return (\n    <div>\n      {mutation.isPending ? (\n        'Adding todo...'\n      ) : (\n        <>\n          {mutation.isError ? (\n            <div>An error occurred: {mutation.error.message}</div>\n          ) : null}\n\n          {mutation.isSuccess ? <div>Todo added!</div> : null}\n\n          <button\n            onClick={() => {\n              mutation.mutate({ title: 'Do Laundry', content: 'content...' })\n            }}\n          >\n            create Todo\n          </button>\n        </>\n      )}\n    </div>\n  )\n}\n\n// usage outside of react component\nuseAddTodo.mutationFn({ title: 'Do Laundry', content: 'content...' })\n```\n\n### Additional API Reference\n\nOptions\n\n- `use: Middleware[]`\n  - Optional\n  - array of middleware functions [(details)](#middleware)\n\nExpose Methods\n\n- `getKey: () => MutationKey`\n- `getOptions: () => UseMutationOptions`\n- `mutationFn: ExposeMutationFn<TData, TVariables>`\n\n## router\n\n`router` which allow you to create a shape of your entire API\n\n### Usage\n\n```tsx\nimport { router } from 'react-query-kit'\n\nconst post = router(`post`, {\n  byId: router.query({\n    fetcher: (variables: { id: number }) =>\n      fetch(`/posts/${variables.id}`).then(res => res.json()),\n    use: [myMiddleware],\n  }),\n\n  list: router.infiniteQuery({\n    fetcher: (_variables, { pageParam }) =>\n      fetch(`/posts/?cursor=${pageParam}`).then(res => res.json()),\n    getNextPageParam: lastPage => lastPage.nextCursor,\n    initialPageParam: 0,\n  }),\n\n  add: router.mutation({\n    mutationFn: async (variables: { title: string; content: string }) =>\n      fetch('/posts', {\n        method: 'POST',\n        body: JSON.stringify(variables),\n      }).then(res => res.json()),\n  }),\n\n  // nest router\n  command: {\n    report: router.mutation({ mutationFn }),\n\n    promote: router.mutation({ mutationFn }),\n  },\n})\n\n// get root key\npost.getKey() // ['post']\n\n// hooks\npost.byId.useQuery({ variables: { id: 1 } })\npost.byId.useSuspenseQuery({ variables: { id: 1 } })\npost.list.useInfiniteQuery()\npost.list.useSuspenseInfiniteQuery()\npost.add.useMutation()\npost.command.report.useMutation()\n\n// expose methods\npost.byId.getKey({ id: 1 }) // ['post', 'byId', { id: 1 }]\npost.byId.getFetchOptions({ id: 1 })\npost.byId.getOptions({ id: 1 })\npost.byId.fetcher({ id: 1 })\npost.add.getKey() // ['post', 'add']\npost.add.getOptions()\npost.add.mutationFn({ title: 'title', content: 'content' })\n\n// infer types\ntype Data = inferData<typeof post.list>\ntype FnData = inferFnData<typeof post.list>\ntype Variables = inferVariables<typeof post.list>\ntype Error = inferError<typeof post.list>\n```\n\n### Merging Routers\n\n```ts\nimport { router } from 'react-query-kit'\n\nconst user = router(`user`, {})\nconst post = router(`post`, {})\n\nconst k = {\n  user,\n  post,\n}\n```\n\n### API Reference\n\n`type Router = (key: string | unknown[], config: TConfig) => TRouter`\n\nExpose Methods\n\n- `query`\n  Similar to `createQuery` but without option `queryKey`\n- `infiniteQuery`\n  Similar to `createInfiniteQuery` but without option `queryKey`\n- `mutation`\n  Similar to `createMutation` but without option `mutationKey`\n\n## Middleware\n\nThis feature is inspired by the [Middleware feature from SWR](https://swr.vercel.app/docs/middleware). The middleware feature is a new addition in ReactQueryKit 1.5.0 that enables you to execute logic before and after hooks.\n\nMiddleware receive the hook and can execute logic before and after running it. If there are multiple middleware, each middleware wraps the next middleware. The last middleware in the list will receive the original hook.\n\n### Usage\n\n```ts\nimport { QueryClient } from '@tanstack/react-query'\nimport { Middleware, MutationHook, QueryHook, getKey } from 'react-query-kit'\n\nconst logger: Middleware<QueryHook<Data, Variables>> = useQueryNext => {\n  return options => {\n    const log = useLogger()\n    const fetcher = (variables, context) => {\n      log(context.queryKey, variables)\n      return options.fetcher(variables, context)\n    }\n\n    return useQueryNext({\n      ...options,\n      fetcher,\n    })\n  }\n}\n\nconst useUser = createQuery<Data, Variables>({\n  use: [logger],\n})\n\n// global middlewares\nconst queryMiddleware: Middleware<QueryHook> = useQueryNext => {\n  return options => {\n    // u can also get queryKey via function getKey\n    const fullKey = getKey(options.queryKey, options.variables)\n    // ...\n    return useQueryNext(options)\n  }\n}\nconst mutationMiddleware: Middleware<MutationHook> = useMutationNext => {\n  return options => {\n    // ...\n    return useMutationNext(options)\n  }\n}\n\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      use: [queryMiddleware],\n    },\n    mutations: {\n      use: [mutationMiddleware],\n    },\n  },\n})\n```\n\n### Extend\n\nMiddleware will be merged from superior. For example:\n\n```jsx\nconst queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      use: [a],\n    },\n  },\n})\n\nconst useSomething = createQuery({\n  use: [b],\n})\n\nuseSomething({ use: [c] })\n```\n\nis equivalent to:\n\n```js\ncreateQuery({ use: [a, b, c] })\n```\n\n### Multiple Middleware\n\nEach middleware wraps the next middleware, and the last one just wraps the useQuery. For example:\n\n```jsx\ncreateQuery({ use: [a, b, c] })\n```\n\nThe order of middleware executions will be a → b → c, as shown below:\n\n```plaintext\nenter a\n  enter b\n    enter c\n      useQuery()\n    exit  c\n  exit  b\nexit  a\n```\n\n### Multiple QueryClient\n\nIn ReactQuery v5, the `QueryClient` will be the second argument to `useQuery` and `useMutation`. If u have multiple `QueryClient` in global, u should receive `QueryClient` in middleware hook.\n\n```ts\nconst useSomething = createQuery({\n  use: [\n    function myMiddleware(useQueryNext) {\n      // u should receive queryClient as the second argument here\n      return (options, queryClient) => {\n        const client = useQueryClient(queryClient)\n        // ...\n        return useQueryNext(options, queryClient)\n      }\n    },\n  ],\n})\n\n// if u need to pass an another QueryClient\nuseSomething({...}, anotherQueryClient)\n```\n\n## TypeScript\n\nBy default, ReactQueryKit will also infer the types of `data` and `variables` from `fetcher`, so you can have the preferred types automatically.\n\n```ts\ntype Data = { title: string; content: string }\ntype Variables = { id: number }\n\nconst usePost = createQuery({\n  queryKey: ['posts'],\n  fetcher: (variables: Variables): Promise<Data> => {\n    return fetch(`/posts/${variables}`).then(res => res.json())\n  },\n})\n\n// `data` will be inferred as `Data | undefined`.\n// `variables` will be inferred as `Variables`.\nconst { data } = usePost({ variables: { id: 1 } })\n```\n\nYou can also explicitly specify the types for `fetcher`‘s `variables` and `data`.\n\n```ts\ntype Data = { title: string; content: string }\ntype Variables = { id: number }\n\nconst usePost = createQuery<Data, Variables, Error>({\n  queryKey: ['posts'],\n  fetcher: variables => {\n    return fetch(`/posts/${variables}`).then(res => res.json())\n  },\n})\n\n// `data` will be inferred as `Data | undefined`.\n// `error` will be inferred as `Error | null`\n// `variables` will be inferred as `Variables`.\nconst { data, error } = usePost({ variables: { id: 1 } })\n```\n\n## Type inference\n\nYou can extract the TypeScript type of any custom hook with `inferData` or `inferVariables`\n\n```ts\nimport { inferData, inferFnData, inferError, inferVariables, inferOptions } from 'react-query-kit'\n\nconst useProjects = createInfiniteQuery<Data, Variables, Error>(...)\n\ninferData<typeof useProjects> // InfiniteData<Data>\ninferFnData<typeof useProjects> // Data\ninferVariables<typeof useProjects> // Variables\ninferError<typeof useProjects> // Error\ninferOptions<typeof useProjects> // InfiniteQueryHookOptions<...>\n```\n\n## Disabling Queries\n\nTo disable queries, you can pass `skipToken` as the option `variables` to your custom query. This will prevent the query from being executed.\n\n```ts\nimport { skipToken } from '@tanstack/react-query'\n\nconst [name, setName] = useState<string | undefined>()\nconst result = usePost({\n  variables: id ? { id: id } : skipToken,\n})\n\n// and for useQueries example\nconst queries = useQueries({\n  queries: [usePost.getOptions(id ? { id: id } : skipToken)],\n})\n```\n\n## FAQ\n\n### What is the difference between `getFetchOptions` and `getOptions`?\n\n`getFetchOptions` would only return necessary options, while options like `staleTime` and `retry` would be omited\n\n### What is the difference between `fetcher` and `queryFn`?\n\nReactQueryKit would automatically converts fetcher to queryFn, as shown below:\n\n```ts\nconst useTest = createQuery({\n  queryKey: ['test'],\n  fetcher: (variables, context) => {\n    // ...\n  },\n})\n\n// => useTest.getOptions(variables):\n// {\n//   queryKey: ['test', variables],\n//   queryFn: (context) => fetcher(variables, context)\n// }\n```\n\n## Migration\n\nUpgrading from ReactQueryKit 2 → ReactQueryKit 3\n\n```diff\ncreateQuery({\n-  primaryKey: 'posts',\n-  queryFn: ({ queryKey: [_primaryKey, variables] }) => {},\n+  queryKey: ['posts'],\n+  fetcher: variables => {},\n})\n```\n\nWhat you benefit from ReactQueryKit 3\n\n- Support hierarchical key\n- Support infer the types of fetcher, you can enjoy the preferred types automatically.\n- Support to create a shape of your entire API\n\n## Issues\n\n_Looking to contribute? Look for the [Good First Issue][good-first-issue]\nlabel._\n\n### 🐛 Bugs\n\nPlease file an issue for bugs, missing documentation, or unexpected behavior.\n\n[**See Bugs**][bugs]\n\n### 💡 Feature Requests\n\nPlease file an issue to suggest new features. Vote on feature requests by adding\na 👍. This helps maintainers prioritize what to work on.\n\n[**See Feature Requests**][requests]\n\n## LICENSE\n\nMIT\n\n<!-- prettier-ignore-start -->\n[npm]: https://www.npmjs.com\n[node]: https://nodejs.org\n[bugs]: https://github.com/liaoliao666/react-query-kit/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug\n[requests]: https://github.com/liaoliao666/react-query-kit/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement\n[good-first-issue]: https://github.com/liaoliao666/react-query-kit/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement+label%3A%22good+first+issue%22\n<!-- prettier-ignore-end -->\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: [\n    [\n      '@babel/preset-env',\n      {\n        loose: true,\n        modules: false,\n        exclude: [\n          '@babel/plugin-transform-regenerator',\n          '@babel/plugin-transform-parameters',\n        ],\n      },\n    ],\n    '@babel/preset-typescript',\n  ],\n}\n"
  },
  {
    "path": "jest.config.js",
    "content": "/** @type {import('jest').Config} */\nconst config = {\n  transform: {\n    '\\\\.[jt]sx?$': 'ts-jest',\n  },\n  testEnvironment: 'jsdom',\n}\n\nmodule.exports = config\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-query-kit\",\n  \"version\": \"3.3.3\",\n  \"description\": \"🕊️ A toolkit for ReactQuery that make ReactQuery hooks more reusable and typesafe\",\n  \"author\": \"liaoliao666\",\n  \"repository\": \"liaoliao666/react-query-kit\",\n  \"homepage\": \"https://github.com/liaoliao666/react-query-kit#readme\",\n  \"types\": \"build/lib/index.d.ts\",\n  \"main\": \"build/lib/index.js\",\n  \"module\": \"build/lib/index.esm.js\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./build/lib/index.d.ts\",\n      \"import\": \"./build/lib/index.mjs\",\n      \"default\": \"./build/lib/index.js\"\n    },\n    \"./package.json\": \"./package.json\"\n  },\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.18.10\",\n    \"@babel/preset-env\": \"^7.18.10\",\n    \"@babel/preset-typescript\": \"^7.18.6\",\n    \"@commitlint/cli\": \"^17.0.3\",\n    \"@commitlint/config-conventional\": \"^17.0.3\",\n    \"@rollup/plugin-babel\": \"^5.3.1\",\n    \"@rollup/plugin-commonjs\": \"^22.0.2\",\n    \"@rollup/plugin-node-resolve\": \"^13.2.1\",\n    \"@rollup/plugin-replace\": \"^4.0.0\",\n    \"@tanstack/react-query\": \"^5.100.6\",\n    \"@testing-library/jest-dom\": \"^5.16.5\",\n    \"@testing-library/react\": \"^14.0.0\",\n    \"@trivago/prettier-plugin-sort-imports\": \"^4.2.0\",\n    \"@types/jest\": \"^28.1.6\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.32.0\",\n    \"@typescript-eslint/parser\": \"^5.32.0\",\n    \"eslint\": \"^8.21.0\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-plugin-import\": \"^2.26.0\",\n    \"eslint-plugin-jest\": \"^26.8.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"eslint-plugin-react\": \"^7.30.1\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"husky\": \"^8.0.1\",\n    \"jest\": \"^29.5.0\",\n    \"jest-environment-jsdom\": \"^29.5.0\",\n    \"prettier\": \"^2.7.1\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"replace\": \"^1.2.1\",\n    \"rollup\": \"^2.77.2\",\n    \"rollup-plugin-size\": \"^0.2.2\",\n    \"rollup-plugin-terser\": \"^7.0.2\",\n    \"rollup-plugin-visualizer\": \"^5.7.1\",\n    \"ts-jest\": \"^29.1.0\",\n    \"typescript\": \"^5.1.6\"\n  },\n  \"peerDependencies\": {\n    \"@tanstack/react-query\": \"^4 || ^5\"\n  },\n  \"peerDependenciesMeta\": {\n    \"@tanstack/react-query\": {\n      \"optional\": true\n    }\n  },\n  \"sideEffects\": false,\n  \"scripts\": {\n    \"build\": \"rollup --config rollup.config.js && npm run typecheck\",\n    \"typecheck\": \"tsc -b && tsc -p tsconfig.types.json --pretty false\",\n    \"stats\": \"open ./build/stats-html.html\",\n    \"eslint\": \"eslint --fix '*.{js,json}' '{src,tests,benchmarks}/**/*.{ts,tsx}'\",\n    \"test\": \"jest\"\n  },\n  \"dependencies\": {},\n  \"files\": [\n    \"build/*\",\n    \"src\"\n  ],\n  \"keywords\": [\n    \"react\",\n    \"react-query\"\n  ]\n}\n"
  },
  {
    "path": "prettier.config.js",
    "content": "module.exports = {\n  printWidth: 80,\n  tabWidth: 2,\n  useTabs: false,\n  semi: false,\n  singleQuote: true,\n  trailingComma: 'es5',\n  bracketSpacing: true,\n  jsxBracketSameLine: false,\n  arrowParens: 'avoid',\n  endOfLine: 'auto',\n  plugins: [require('@trivago/prettier-plugin-sort-imports')],\n  importOrder: ['^[./]'],\n  importOrderSeparation: true,\n  importOrderSortSpecifiers: true,\n}\n"
  },
  {
    "path": "rollup.config.js",
    "content": "import { babel } from '@rollup/plugin-babel'\nimport commonJS from '@rollup/plugin-commonjs'\nimport { nodeResolve } from '@rollup/plugin-node-resolve'\nimport replace from '@rollup/plugin-replace'\nimport size from 'rollup-plugin-size'\nimport { terser } from 'rollup-plugin-terser'\nimport visualizer from 'rollup-plugin-visualizer'\n\nconst replaceDevPlugin = type =>\n  replace({\n    'process.env.NODE_ENV': `\"${type}\"`,\n    delimiters: ['', ''],\n    preventAssignment: true,\n  })\nconst extensions = ['.ts', '.tsx']\nconst babelPlugin = babel({\n  babelHelpers: 'bundled',\n  exclude: /node_modules/,\n  extensions,\n})\n\nexport default function rollup() {\n  const options = {\n    input: 'src/index.ts',\n    jsName: 'ReactQueryKit',\n    external: ['@tanstack/react-query'],\n    globals: {\n      '@tanstack/react-query': 'ReactQuery',\n    },\n  }\n\n  return [\n    mjs(options),\n    esm(options),\n    cjs(options),\n    umdDev(options),\n    umdProd(options),\n  ]\n}\n\nfunction mjs({ input, external }) {\n  return {\n    // ESM\n    external,\n    input,\n    output: {\n      format: 'esm',\n      sourcemap: true,\n      dir: `build/lib`,\n      preserveModules: true,\n      entryFileNames: '[name].mjs',\n    },\n    plugins: [babelPlugin, commonJS(), nodeResolve({ extensions })],\n  }\n}\n\nfunction esm({ input, external }) {\n  return {\n    // ESM\n    external,\n    input,\n    output: {\n      format: 'esm',\n      dir: `build/lib`,\n      sourcemap: true,\n      preserveModules: true,\n      entryFileNames: '[name].esm.js',\n    },\n    plugins: [babelPlugin, commonJS(), nodeResolve({ extensions })],\n  }\n}\n\nfunction cjs({ input, external }) {\n  return {\n    // CJS\n    external,\n    input,\n    output: {\n      format: 'cjs',\n      sourcemap: true,\n      dir: `build/lib`,\n      preserveModules: true,\n      exports: 'named',\n      entryFileNames: '[name].js',\n    },\n    plugins: [babelPlugin, commonJS(), nodeResolve({ extensions })],\n  }\n}\n\nfunction umdDev({ input, external, globals, jsName }) {\n  return {\n    // UMD (Dev)\n    external,\n    input,\n    output: {\n      format: 'umd',\n      sourcemap: true,\n      file: `build/umd/index.development.js`,\n      name: jsName,\n      globals,\n    },\n    plugins: [\n      babelPlugin,\n      commonJS(),\n      nodeResolve({ extensions }),\n      replaceDevPlugin('development'),\n    ],\n  }\n}\n\nfunction umdProd({ input, external, globals, jsName }) {\n  return {\n    // UMD (Prod)\n    external,\n    input,\n    output: {\n      format: 'umd',\n      sourcemap: true,\n      file: `build/umd/index.production.js`,\n      name: jsName,\n      globals,\n    },\n    plugins: [\n      babelPlugin,\n      commonJS(),\n      nodeResolve({ extensions }),\n      replaceDevPlugin('production'),\n      terser({\n        mangle: true,\n        compress: true,\n      }),\n      size({}),\n      visualizer({\n        filename: `build/stats-html.html`,\n        gzipSize: true,\n      }),\n      visualizer({\n        filename: `build/stats.json`,\n        json: true,\n        gzipSize: true,\n      }),\n    ],\n  }\n}\n"
  },
  {
    "path": "src/createBaseQuery.ts",
    "content": "import {\n  type QueryClient,\n  type QueryFunctionContext,\n  type UseBaseQueryOptions,\n  type UseInfiniteQueryOptions,\n} from '@tanstack/react-query'\n\nimport { ReactQuery, getKey as getFullKey, withMiddleware } from './utils'\n\ntype QueryBaseHookOptions = Omit<\n  UseBaseQueryOptions,\n  'queryKey' | 'queryFn'\n> & {\n  fetcher?: any\n  variables?: any\n}\n\nexport const createBaseQuery = (\n  defaultOptions: any,\n  useRQHook: (options: any, queryClient?: any) => any,\n  overrideOptions?: Partial<UseInfiniteQueryOptions>\n): any => {\n  if (process.env.NODE_ENV !== 'production') {\n    // @ts-ignore\n    if (defaultOptions.useDefaultOptions) {\n      console.error(\n        '[Bug] useDefaultOptions is not supported, please use middleware instead.'\n      )\n    }\n\n    // @ts-ignore\n    if (defaultOptions.queryFn) {\n      console.error(\n        '[Bug] queryFn is not supported, please use fetcher instead.'\n      )\n    }\n  }\n\n  const getQueryOptions = (fetcherFn: any, variables: any) => {\n    return {\n      queryFn:\n        variables && variables === ReactQuery.skipToken\n          ? ReactQuery.skipToken\n          : (context: QueryFunctionContext) => fetcherFn(variables, context),\n      queryKey: getFullKey(defaultOptions.queryKey, variables),\n    }\n  }\n\n  const getKey = (variables?: any) =>\n    getFullKey(defaultOptions.queryKey, variables)\n\n  const getOptions = (variables: any) => {\n    return {\n      ...defaultOptions,\n      ...getQueryOptions(defaultOptions.fetcher, variables),\n    }\n  }\n\n  const getFetchOptions = (variables: any) => {\n    return {\n      ...getQueryOptions(defaultOptions.fetcher, variables),\n      queryKeyHashFn: defaultOptions.queryKeyHashFn,\n      getPreviousPageParam: defaultOptions.getPreviousPageParam,\n      getNextPageParam: defaultOptions.getNextPageParam,\n      initialPageParam: defaultOptions.initialPageParam,\n    }\n  }\n\n  const useBaseHook = (\n    options: QueryBaseHookOptions,\n    queryClient?: QueryClient\n  ) => {\n    return useRQHook(\n      {\n        ...options,\n        ...getQueryOptions(options.fetcher, options.variables),\n        ...overrideOptions,\n      },\n      queryClient\n    )\n  }\n\n  return Object.assign(withMiddleware(useBaseHook, defaultOptions, 'queries'), {\n    fetcher: defaultOptions.fetcher,\n    getKey,\n    getOptions,\n    getFetchOptions,\n  })\n}\n"
  },
  {
    "path": "src/createInfiniteQuery.ts",
    "content": "import { createBaseQuery } from './createBaseQuery'\nimport type {\n  CompatibleError,\n  CreateInfiniteQueryOptions,\n  InfiniteQueryHook,\n} from './types'\nimport { ReactQuery } from './utils'\n\nexport function createInfiniteQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n>(\n  options: CreateInfiniteQueryOptions<TFnData, TVariables, TError, TPageParam>\n): InfiniteQueryHook<TFnData, TVariables, TError, TPageParam> {\n  return createBaseQuery(options, ReactQuery.useInfiniteQuery)\n}\n"
  },
  {
    "path": "src/createMutation.ts",
    "content": "import type {\n  CompatibleError,\n  CreateMutationOptions,\n  MutationHook,\n} from './types'\nimport { ReactQuery, withMiddleware } from './utils'\n\nexport function createMutation<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TContext = unknown\n>(\n  defaultOptions: CreateMutationOptions<TData, TVariables, TError, TContext>\n): MutationHook<TData, TVariables, TError, TContext> {\n  return Object.assign(\n    withMiddleware(ReactQuery.useMutation, defaultOptions, 'mutations'),\n    {\n      getKey: () => defaultOptions.mutationKey,\n      getOptions: () => defaultOptions,\n      mutationFn: defaultOptions.mutationFn,\n    }\n  ) as MutationHook<TData, TVariables, TError, TContext>\n}\n"
  },
  {
    "path": "src/createQuery.ts",
    "content": "import { createBaseQuery } from './createBaseQuery'\nimport type { CompatibleError, CreateQueryOptions, QueryHook } from './types'\nimport { ReactQuery } from './utils'\n\nexport function createQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError\n>(\n  options: CreateQueryOptions<TFnData, TVariables, TError>\n): QueryHook<TFnData, TVariables, TError> {\n  return createBaseQuery(options, ReactQuery.useQuery)\n}\n"
  },
  {
    "path": "src/createSuspenseInfiniteQuery.ts",
    "content": "import { createBaseQuery } from './createBaseQuery'\nimport type {\n  CompatibleError,\n  CreateSuspenseInfiniteQueryOptions,\n  SuspenseInfiniteQueryHook,\n} from './types'\nimport { ReactQuery, isV5, suspenseOptions } from './utils'\n\nexport function createSuspenseInfiniteQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n>(\n  options: CreateSuspenseInfiniteQueryOptions<\n    TFnData,\n    TVariables,\n    TError,\n    TPageParam\n  >\n): SuspenseInfiniteQueryHook<TFnData, TVariables, TError, TPageParam> {\n  return isV5\n    ? createBaseQuery(options, ReactQuery.useSuspenseInfiniteQuery)\n    : createBaseQuery(options, ReactQuery.useInfiniteQuery, suspenseOptions)\n}\n"
  },
  {
    "path": "src/createSuspenseQuery.ts",
    "content": "import { createBaseQuery } from './createBaseQuery'\nimport type {\n  CompatibleError,\n  CreateSuspenseQueryOptions,\n  SuspenseQueryHook,\n} from './types'\nimport { ReactQuery, isV5, suspenseOptions } from './utils'\n\nexport function createSuspenseQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError\n>(\n  options: CreateSuspenseQueryOptions<TFnData, TVariables, TError>\n): SuspenseQueryHook<TFnData, TVariables, TError> {\n  return isV5\n    ? createBaseQuery(options, ReactQuery.useSuspenseQuery)\n    : createBaseQuery(options, ReactQuery.useQuery, suspenseOptions)\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export * from './createQuery'\nexport * from './createSuspenseQuery'\nexport * from './createInfiniteQuery'\nexport * from './createSuspenseInfiniteQuery'\nexport * from './createMutation'\nexport * from './types'\nexport * from './router'\nexport { getKey } from './utils'\n"
  },
  {
    "path": "src/router.ts",
    "content": "import { QueryKey } from '@tanstack/react-query'\n\nimport { createInfiniteQuery } from './createInfiniteQuery'\nimport { createMutation } from './createMutation'\nimport { createQuery } from './createQuery'\nimport { createSuspenseInfiniteQuery } from './createSuspenseInfiniteQuery'\nimport { createSuspenseQuery } from './createSuspenseQuery'\nimport type {\n  CompatibleError,\n  CreateRouter,\n  RouterConfig,\n  RouterInfiniteQuery,\n  RouterInfiniteQueryOptions,\n  RouterMutation,\n  RouterMutationOptions,\n  RouterQuery,\n  RouterQueryOptions,\n} from './types'\n\ntype RouterNode = RouterConfig[string]\ntype RouterTypedNode = RouterNode & {\n  _routerType: 'q' | 'inf' | 'm'\n}\n\nconst isRouterLeaf = (value: RouterNode): value is RouterTypedNode => {\n  return !!value && typeof value._routerType === 'string'\n}\n\nconst buildRouter = (keys: QueryKey, config: RouterConfig) => {\n  return Object.entries(config).reduce(\n    (acc, [key, opts]) => {\n      if (!isRouterLeaf(opts)) {\n        acc[key] = buildRouter([...keys, key], opts)\n      } else {\n        const type = opts._routerType\n        const options: any = {\n          ...opts,\n          [type === `m` ? `mutationKey` : `queryKey`]: [...keys, key],\n        }\n\n        if (type === `m`) {\n          acc[key] = {\n            useMutation: createMutation(options),\n            ...createMutation(options),\n          }\n          return acc\n        }\n\n        acc[key] =\n          type === `q`\n            ? {\n                useQuery: createQuery(options),\n                useSuspenseQuery: createSuspenseQuery(options),\n                ...createQuery(options),\n              }\n            : {\n                useInfiniteQuery: createInfiniteQuery(options),\n                useSuspenseInfiniteQuery: createSuspenseInfiniteQuery(options),\n                ...createInfiniteQuery(options),\n              }\n      }\n\n      return acc\n    },\n    {\n      getKey: () => keys,\n    } as any\n  )\n}\n\nexport const router = <TConfig extends RouterConfig>(\n  key: string | QueryKey,\n  config: TConfig\n): CreateRouter<TConfig> => {\n  return buildRouter(Array.isArray(key) ? key : [key], config)\n}\n\nfunction query<TFnData, TVariables = void, TError = CompatibleError>(\n  options: RouterQueryOptions<TFnData, TVariables, TError>\n): RouterQuery<TFnData, TVariables, TError> {\n  return {\n    ...options,\n    _routerType: 'q',\n  }\n}\n\nfunction infiniteQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n>(\n  options: RouterInfiniteQueryOptions<TFnData, TVariables, TError, TPageParam>\n): RouterInfiniteQuery<TFnData, TVariables, TError, TPageParam> {\n  return { ...options, _routerType: 'inf' } as RouterInfiniteQuery<\n    TFnData,\n    TVariables,\n    TError,\n    TPageParam\n  >\n}\n\nfunction mutation<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TContext = unknown\n>(\n  options: RouterMutationOptions<TFnData, TVariables, TError, TContext>\n): RouterMutation<TFnData, TVariables, TError, TContext> {\n  return { ...options, _routerType: 'm' } as RouterMutation<\n    TFnData,\n    TVariables,\n    TError,\n    TContext\n  >\n}\n\nrouter.query = query\nrouter.infiniteQuery = infiniteQuery\nrouter.mutation = mutation\n"
  },
  {
    "path": "src/types.ts",
    "content": "import type {\n  DataTag,\n  DefaultError,\n  DefinedUseInfiniteQueryResult,\n  DefinedUseQueryResult,\n  InfiniteData,\n  InfiniteQueryObserverSuccessResult,\n  MutationFunctionContext,\n  MutationKey,\n  QueryClient,\n  QueryFunction,\n  QueryFunctionContext,\n  QueryKey,\n  QueryKeyHashFunction,\n  QueryObserverSuccessResult,\n  SkipToken,\n  UseInfiniteQueryOptions,\n  UseInfiniteQueryResult,\n  UseMutationOptions,\n  UseMutationResult,\n  UseQueryOptions,\n  UseQueryResult,\n} from '@tanstack/react-query'\n\n// utils\n\ntype CompatibleWithV4<V5, V4> =\n  InfiniteData<unknown> extends UseInfiniteQueryResult<\n    InfiniteData<unknown>\n  >['data']\n    ? V5\n    : V4\n\ntype CompatibleWithTwoV5<V6Generics, V5Generics> = UseInfiniteQueryOptions<\n  unknown,\n  DefaultError,\n  unknown,\n  QueryKey,\n  unknown\n> extends never\n  ? V5Generics\n  : V6Generics\n\ntype CompatibleUseInfiniteQueryOptions<TFnData, TData, TError, TPageParam> =\n  CompatibleWithV4<\n    CompatibleWithTwoV5<\n      UseInfiniteQueryOptions<TFnData, TError, TData, QueryKey, TPageParam>,\n      // @ts-ignore\n      UseInfiniteQueryOptions<\n        TFnData,\n        TError,\n        TData,\n        TFnData,\n        QueryKey,\n        TPageParam\n      >\n    >,\n    // @ts-ignore\n    UseInfiniteQueryOptions<TFnData, TError, TData, TFnData, QueryKey>\n  >\n\ntype CompatibleInfiniteData<TFnData, TPageParam> = CompatibleWithV4<\n  InfiniteData<TFnData, TPageParam>,\n  InfiniteData<TFnData>\n>\n\ntype NonUndefinedGuard<T> = T extends undefined ? never : T\n\ntype WithRequired<T, K extends keyof T> = T & {\n  [_ in K]: {}\n}\n\ntype DeepPartial<T> = T extends object\n  ? {\n      [P in keyof T]?: DeepPartial<T[P]>\n    }\n  : T\n\ntype DefaultTo<T, D> = unknown extends T ? D : T\n\nexport type CompatibleError = CompatibleWithV4<DefaultError, Error>\n\nexport type Fetcher<TFnData, TVariables = void, TPageParam = never> = (\n  variables: TVariables,\n  context: QueryFunctionContext<QueryKey, TPageParam>\n) => TFnData | Promise<TFnData>\n\nexport type AdditionalQueryOptions<TFnData, TVariables, TPageParam = never> = {\n  fetcher: Fetcher<TFnData, TVariables, TPageParam>\n  variables?: TVariables\n}\n\ntype inferMiddlewareHook<T extends (...args: any) => any> = (\n  options: inferCreateOptions<T>,\n  queryClient?: CompatibleWithV4<QueryClient, void>\n) => ReturnType<T>\n\nexport type Middleware<\n  T extends (...args: any) => any = QueryHook<any, any, any>\n> = (hook: inferMiddlewareHook<T>) => inferMiddlewareHook<T>\n\nexport type ExposeFetcher<TFnData, TVariables = void, TPageParam = never> = (\n  variables: TVariables,\n  context?: Partial<QueryFunctionContext<QueryKey, TPageParam>>\n) => TFnData | Promise<TFnData>\n\nexport type ExposeMethods<TFnData, TVariables, TError, TPageParam = never> = {\n  fetcher: ExposeFetcher<TFnData, TVariables, TPageParam>\n  getKey: (\n    variables?: DeepPartial<TVariables>\n  ) => CompatibleWithV4<\n    DataTag<\n      QueryKey,\n      [TPageParam] extends [never] ? TFnData : InfiniteData<TFnData, TPageParam>\n    >,\n    QueryKey\n  >\n  getFetchOptions: (\n    variables: TVariables extends void\n      ? CompatibleWithV4<TVariables | SkipToken, TVariables> | void\n      : CompatibleWithV4<TVariables | SkipToken, TVariables>\n  ) => Pick<\n    ReturnType<\n      ExposeMethods<TFnData, TVariables, TError, TPageParam>['getOptions']\n    >,\n    // @ts-ignore\n    [TPageParam] extends [never]\n      ? 'queryKey' | 'queryFn' | 'queryKeyHashFn'\n      :\n          | 'queryKey'\n          | 'queryFn'\n          | 'queryKeyHashFn'\n          | 'getNextPageParam'\n          | 'getPreviousPageParam'\n          | 'initialPageParam'\n  >\n  getOptions: (\n    variables: TVariables extends void\n      ? CompatibleWithV4<TVariables | SkipToken, TVariables> | void\n      : CompatibleWithV4<TVariables | SkipToken, TVariables>\n  ) => [TPageParam] extends [never]\n    ? CompatibleWithV4<\n        UseQueryOptions<TFnData, TError, TFnData, QueryKey> & {\n          queryKey: DataTag<QueryKey, TFnData>\n          queryFn?: Exclude<\n            UseQueryOptions<TFnData, TError, TFnData, QueryKey>['queryFn'],\n            SkipToken\n          >\n        },\n        // Not work to infer TError in v4\n        {\n          queryKey: QueryKey\n          queryFn: QueryFunction<TFnData, QueryKey>\n          queryKeyHashFn?: QueryKeyHashFunction<QueryKey>\n        }\n      >\n    : CompatibleUseInfiniteQueryOptions<\n        TFnData,\n        TFnData,\n        TError,\n        TPageParam\n      > & {\n        queryKey: CompatibleWithV4<\n          DataTag<QueryKey, InfiniteData<TFnData, TPageParam>>,\n          QueryKey\n        >\n      }\n}\n\ntype Clone<T> = T extends infer TClone ? TClone : never\n\n// query hook\n\nexport interface CreateQueryOptions<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError\n> extends Omit<\n      UseQueryOptions<TFnData, TError, TFnData, QueryKey>,\n      'queryKey' | 'queryFn' | 'select'\n    >,\n    AdditionalQueryOptions<TFnData, TVariables> {\n  queryKey: QueryKey\n  use?: Middleware<\n    QueryHook<Clone<TFnData>, Clone<TVariables>, Clone<TError>>\n  >[]\n  variables?: TVariables\n}\n\nexport interface QueryHookOptions<TFnData, TError, TData, TVariables>\n  extends Omit<\n    UseQueryOptions<TFnData, TError, TData, QueryKey>,\n    'queryKey' | 'queryFn' | 'queryKeyHashFn'\n  > {\n  use?: Middleware<QueryHook<TFnData, TVariables, TError>>[]\n  variables?: CompatibleWithV4<TVariables | SkipToken, TVariables>\n}\n\nexport interface DefinedQueryHookOptions<TFnData, TError, TData, TVariables>\n  extends Omit<\n    QueryHookOptions<TFnData, TError, TData, TVariables>,\n    'initialData'\n  > {\n  initialData: NonUndefinedGuard<TFnData> | (() => NonUndefinedGuard<TFnData>)\n}\n\nexport type QueryHookResult<TData, TError> = UseQueryResult<TData, TError>\n\nexport type DefinedQueryHookResult<TData, TError> = DefinedUseQueryResult<\n  TData,\n  TError\n>\n\nexport interface QueryHook<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError\n> extends ExposeMethods<TFnData, TVariables, TError> {\n  <TData = TFnData>(\n    options: DefinedQueryHookOptions<TFnData, TError, TData, TVariables>,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): DefinedQueryHookResult<TData, TError>\n  <TData = TFnData>(\n    options?: QueryHookOptions<TFnData, TError, TData, TVariables>,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): QueryHookResult<TData, TError>\n}\n\n// suspense query hook\n\nexport interface CreateSuspenseQueryOptions<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError\n> extends Omit<\n      UseQueryOptions<TFnData, TError, TFnData, QueryKey>,\n      | 'queryKey'\n      | 'queryFn'\n      | 'enabled'\n      | 'select'\n      | 'suspense'\n      | 'throwOnError'\n      | 'placeholderData'\n      | 'keepPreviousData'\n      | 'useErrorBoundary'\n    >,\n    AdditionalQueryOptions<TFnData, TVariables> {\n  queryKey: QueryKey\n  use?: Middleware<\n    SuspenseQueryHook<Clone<TFnData>, Clone<TVariables>, Clone<TError>>\n  >[]\n  variables?: TVariables\n}\n\nexport interface SuspenseQueryHookOptions<TFnData, TError, TData, TVariables>\n  extends Omit<\n    UseQueryOptions<TFnData, TError, TData, QueryKey>,\n    | 'queryKey'\n    | 'queryFn'\n    | 'queryKeyHashFn'\n    | 'enabled'\n    | 'suspense'\n    | 'throwOnError'\n    | 'placeholderData'\n    | 'keepPreviousData'\n    | 'useErrorBoundary'\n  > {\n  use?: Middleware<SuspenseQueryHook<TFnData, TVariables, TVariables>>[]\n  variables?: CompatibleWithV4<TVariables | SkipToken, TVariables>\n}\n\nexport type SuspenseQueryHookResult<TData, TError> = Omit<\n  QueryObserverSuccessResult<TData, TError>,\n  'isPlaceholderData' | 'isPreviousData'\n>\n\nexport interface SuspenseQueryHook<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError\n> extends ExposeMethods<TFnData, TVariables, TError> {\n  <TData = TFnData>(\n    options?: SuspenseQueryHookOptions<TFnData, TError, TData, TVariables>,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): SuspenseQueryHookResult<TData, TError>\n}\n\n// infinite query hook\n\nexport interface CreateInfiniteQueryOptions<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> extends Omit<\n      CompatibleUseInfiniteQueryOptions<TFnData, TFnData, TError, TPageParam>,\n      'queryKey' | 'queryFn' | 'select'\n    >,\n    AdditionalQueryOptions<TFnData, TVariables, TPageParam> {\n  queryKey: QueryKey\n  use?: Middleware<\n    InfiniteQueryHook<\n      Clone<TFnData>,\n      Clone<TVariables>,\n      Clone<TError>,\n      Clone<TPageParam>\n    >\n  >[]\n  variables?: TVariables\n}\n\nexport interface InfiniteQueryHookOptions<\n  TFnData,\n  TError,\n  TData,\n  TVariables,\n  TPageParam = number\n> extends Omit<\n    CompatibleUseInfiniteQueryOptions<TFnData, TData, TError, TPageParam>,\n    | 'queryKey'\n    | 'queryFn'\n    | 'queryKeyHashFn'\n    | 'initialPageParam'\n    | 'getPreviousPageParam'\n    | 'getNextPageParam'\n  > {\n  use?: Middleware<InfiniteQueryHook<TFnData, TVariables, TError, TPageParam>>[]\n  variables?: CompatibleWithV4<TVariables | SkipToken, TVariables>\n}\n\nexport interface DefinedInfiniteQueryHookOptions<\n  TFnData,\n  TError,\n  TData,\n  TVariables,\n  TPageParam = number\n> extends Omit<\n    InfiniteQueryHookOptions<TFnData, TError, TData, TVariables, TPageParam>,\n    'initialData'\n  > {\n  initialData:\n    | NonUndefinedGuard<CompatibleInfiniteData<TFnData, TPageParam>>\n    | (() => NonUndefinedGuard<CompatibleInfiniteData<TFnData, TPageParam>>)\n}\n\nexport type InfiniteQueryHookResult<TData, TError> = UseInfiniteQueryResult<\n  TData,\n  TError\n>\n\nexport type DefinedInfiniteQueryHookResult<TData, TError> = CompatibleWithV4<\n  DefinedUseInfiniteQueryResult<TData, TError>,\n  WithRequired<UseInfiniteQueryResult<TData, TError>, 'data'>\n>\n\nexport interface InfiniteQueryHook<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> extends ExposeMethods<TFnData, TVariables, TError, TPageParam> {\n  <TData = CompatibleWithV4<InfiniteData<TFnData, TPageParam>, TFnData>>(\n    options: DefinedInfiniteQueryHookOptions<\n      TFnData,\n      TError,\n      TData,\n      TVariables,\n      TPageParam\n    >,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): DefinedInfiniteQueryHookResult<TData, TError>\n  <TData = CompatibleWithV4<InfiniteData<TFnData, TPageParam>, TFnData>>(\n    options?: InfiniteQueryHookOptions<\n      TFnData,\n      TError,\n      TData,\n      TVariables,\n      TPageParam\n    >,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): InfiniteQueryHookResult<TData, TError>\n}\n\n// infinite sususpense query hook\n\nexport interface CreateSuspenseInfiniteQueryOptions<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> extends Omit<\n      CompatibleUseInfiniteQueryOptions<TFnData, TFnData, TError, TPageParam>,\n      | 'queryKey'\n      | 'queryFn'\n      | 'enabled'\n      | 'select'\n      | 'suspense'\n      | 'throwOnError'\n      | 'placeholderData'\n      | 'keepPreviousData'\n      | 'useErrorBoundary'\n    >,\n    AdditionalQueryOptions<TFnData, TVariables, TPageParam> {\n  queryKey: QueryKey\n  use?: Middleware<\n    SuspenseInfiniteQueryHook<\n      Clone<TFnData>,\n      Clone<TVariables>,\n      Clone<TError>,\n      Clone<TPageParam>\n    >\n  >[]\n  variables?: TVariables\n}\n\nexport interface SuspenseInfiniteQueryHookOptions<\n  TFnData,\n  TError,\n  TData,\n  TVariables,\n  TPageParam = number\n> extends Omit<\n    CompatibleUseInfiniteQueryOptions<TFnData, TData, TError, TPageParam>,\n    | 'queryKey'\n    | 'queryFn'\n    | 'queryKeyHashFn'\n    | 'enabled'\n    | 'initialPageParam'\n    | 'getPreviousPageParam'\n    | 'getNextPageParam'\n    | 'suspense'\n    | 'throwOnError'\n    | 'placeholderData'\n    | 'keepPreviousData'\n    | 'useErrorBoundary'\n  > {\n  use?: Middleware<SuspenseInfiniteQueryHook<TFnData, TVariables, TVariables>>[]\n  variables?: CompatibleWithV4<TVariables | SkipToken, TVariables>\n}\n\nexport type SuspenseInfiniteQueryHookResult<TData, TError> = Omit<\n  InfiniteQueryObserverSuccessResult<TData, TError>,\n  'isPlaceholderData' | 'isPreviousData'\n>\n\nexport interface SuspenseInfiniteQueryHook<\n  TFnData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> extends ExposeMethods<TFnData, TVariables, TError, TPageParam> {\n  <TData = CompatibleWithV4<InfiniteData<TFnData, TPageParam>, TFnData>>(\n    options?: SuspenseInfiniteQueryHookOptions<\n      TFnData,\n      TError,\n      TData,\n      TVariables,\n      TPageParam\n    >,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): SuspenseInfiniteQueryHookResult<TData, TError>\n}\n\n// mutation hook\n\nexport interface CreateMutationOptions<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TContext = unknown\n> extends UseMutationOptions<TData, TError, TVariables, TContext> {\n  use?: Middleware<MutationHook<TData, TVariables, TError>>[]\n}\n\nexport interface MutationHookOptions<TData, TError, TVariables, TContext>\n  extends Omit<\n    UseMutationOptions<TData, TError, TVariables, TContext>,\n    'mutationFn' | 'mutationKey'\n  > {\n  use?: Middleware<MutationHook<TData, TVariables, TError>>[]\n}\n\nexport type MutationHookResult<\n  TData = unknown,\n  TError = CompatibleError,\n  TVariables = void,\n  TContext = unknown\n> = UseMutationResult<TData, TError, TVariables, TContext>\n\nexport type ExposeMutationFn<TData = unknown, TVariables = void> = [\n  TVariables\n] extends [void]\n  ? (\n      variables?: TVariables,\n      context?: Partial<MutationFunctionContext>\n    ) => Promise<TData>\n  : (\n      variables: TVariables,\n      context?: Partial<MutationFunctionContext>\n    ) => Promise<TData>\n\nexport interface ExposeMutationMethods<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TDefaultContext = unknown\n> {\n  getKey: () => MutationKey | undefined\n  getOptions: () => UseMutationOptions<\n    TData,\n    TError,\n    TVariables,\n    TDefaultContext\n  >\n  mutationFn: ExposeMutationFn<TData, TVariables>\n}\n\nexport interface MutationHook<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TDefaultContext = unknown\n> extends ExposeMutationMethods<TData, TVariables, TError, TDefaultContext> {\n  <TContext = TDefaultContext>(\n    options?: MutationHookOptions<TData, TError, TVariables, TContext>,\n    queryClient?: CompatibleWithV4<QueryClient, void>\n  ): MutationHookResult<TData, TError, TVariables, TContext>\n}\n\n// infer types\n\nexport type inferVariables<T> = T extends {\n  fetcher: ExposeFetcher<any, infer TVariables, infer _TPageParam>\n}\n  ? TVariables\n  : T extends ExposeMutationMethods<any, infer TVariables, any, any>\n  ? TVariables\n  : never\n\nexport type inferData<T> = T extends {\n  fetcher: ExposeFetcher<infer TFnData, any, infer TPageParam>\n}\n  ? [TPageParam] extends [never]\n    ? TFnData\n    : CompatibleInfiniteData<TFnData, TPageParam>\n  : T extends ExposeMutationMethods<infer TFnData, any, any, any>\n  ? TFnData\n  : never\n\nexport type inferFnData<T> = T extends {\n  fetcher: ExposeFetcher<infer TFnData, any, infer _TPageParam>\n}\n  ? TFnData\n  : T extends ExposeMutationMethods<infer TFnData, any, any, any>\n  ? TFnData\n  : never\n\nexport type inferError<T> = T extends ExposeMethods<any, any, infer TError>\n  ? TError\n  : T extends ExposeMethods<any, any, infer TError, any>\n  ? TError\n  : T extends ExposeMutationMethods<any, any, infer TError, any>\n  ? TError\n  : never\n\nexport type inferOptions<T> = T extends QueryHook<\n  infer TFnData,\n  infer TVariables,\n  infer TError\n>\n  ? QueryHookOptions<TFnData, TError, TFnData, TVariables>\n  : T extends SuspenseQueryHook<infer TFnData, infer TVariables, infer TError>\n  ? SuspenseQueryHookOptions<TFnData, TError, TFnData, TVariables>\n  : T extends InfiniteQueryHook<\n      infer TFnData,\n      infer TVariables,\n      infer TError,\n      infer TPageParam\n    >\n  ? InfiniteQueryHookOptions<\n      TFnData,\n      TError,\n      CompatibleWithV4<InfiniteData<TFnData, TPageParam>, TFnData>,\n      TVariables,\n      TPageParam\n    >\n  : T extends SuspenseInfiniteQueryHook<\n      infer TFnData,\n      infer TVariables,\n      infer TError,\n      infer TPageParam\n    >\n  ? SuspenseInfiniteQueryHookOptions<\n      TFnData,\n      TError,\n      CompatibleWithV4<InfiniteData<TFnData, TPageParam>, TFnData>,\n      TVariables,\n      TPageParam\n    >\n  : T extends MutationHook<infer TFnData, infer TVariables, infer TError>\n  ? MutationHookOptions<TFnData, TError, TVariables, unknown>\n  : never\n\nexport type inferCreateOptions<T> = T extends QueryHook<\n  infer TFnData,\n  infer TVariables,\n  infer TError\n>\n  ? CreateQueryOptions<TFnData, TVariables, TError>\n  : T extends SuspenseQueryHook<infer TFnData, infer TVariables, infer TError>\n  ? CreateSuspenseQueryOptions<TFnData, TVariables, TError>\n  : T extends InfiniteQueryHook<\n      infer TFnData,\n      infer TVariables,\n      infer TError,\n      infer TPageParam\n    >\n  ? CreateInfiniteQueryOptions<TFnData, TVariables, TError, TPageParam>\n  : T extends SuspenseInfiniteQueryHook<\n      infer TFnData,\n      infer TVariables,\n      infer TError,\n      infer TPageParam\n    >\n  ? CreateSuspenseInfiniteQueryOptions<TFnData, TVariables, TError, TPageParam>\n  : T extends MutationHook<\n      infer TFnData,\n      infer TVariables,\n      infer TError,\n      infer TContext\n    >\n  ? CreateMutationOptions<TFnData, TVariables, TError, TContext>\n  : never\n\n// router\n\nexport type RouterQueryOptions<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError\n> = Omit<CreateQueryOptions<TFnData, TVariables, TError>, 'queryKey'>\n\nexport type RouterQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError\n> = RouterQueryOptions<TFnData, TVariables, TError> & {\n  _routerType: `q`\n}\n\nexport type ResolvedRouterQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError\n> = {\n  useQuery: QueryHook<TFnData, TVariables, TError>\n  useSuspenseQuery: SuspenseQueryHook<TFnData, TVariables, TError>\n} & ExposeMethods<TFnData, TVariables, TError>\n\nexport type RouterInfiniteQueryOptions<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> = Omit<\n  CreateInfiniteQueryOptions<TFnData, TVariables, TError, TPageParam>,\n  'queryKey'\n>\n\nexport type RouterInfiniteQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> = RouterInfiniteQueryOptions<\n  TFnData,\n  TVariables,\n  TError,\n  Clone<TPageParam>\n> & {\n  _routerType: `inf`\n}\n\nexport type ResolvedRouterInfiniteQuery<\n  TFnData,\n  TVariables = void,\n  TError = CompatibleError,\n  TPageParam = number\n> = {\n  useInfiniteQuery: InfiniteQueryHook<TFnData, TVariables, TError, TPageParam>\n  useSuspenseInfiniteQuery: SuspenseInfiniteQueryHook<\n    TFnData,\n    TVariables,\n    TError,\n    TPageParam\n  >\n} & ExposeMethods<TFnData, TVariables, TError, TPageParam>\n\nexport type RouterMutationOptions<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TContext = unknown\n> = Omit<\n  CreateMutationOptions<TData, TVariables, TError, TContext>,\n  'mutationKey'\n>\n\nexport type RouterMutation<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TContext = unknown\n> = RouterMutationOptions<TData, TVariables, TError, TContext> & {\n  _routerType: `m`\n}\n\nexport type ResolvedRouterMutation<\n  TData = unknown,\n  TVariables = void,\n  TError = CompatibleError,\n  TContext = unknown\n> = {\n  useMutation: MutationHook<\n    TData,\n    DefaultTo<TVariables, void>,\n    DefaultTo<TError, CompatibleError>,\n    TContext\n  >\n} & ExposeMutationMethods<\n  TData,\n  DefaultTo<TVariables, void>,\n  DefaultTo<TError, CompatibleError>,\n  TContext\n>\n\nexport type RouterLeaf =\n  | RouterQuery<any, any, any>\n  | RouterInfiniteQuery<any, any, any, any>\n  | RouterMutation<any, any, any, any>\n\nexport type RouterConfig = {\n  _routerType?: never\n} & {\n  [k: string]: RouterLeaf | RouterConfig\n}\n\nexport type CreateRouter<TConfig extends RouterConfig> = {\n  [K in keyof TConfig]: TConfig[K] extends RouterMutation<\n    infer TFnData,\n    infer TVariables,\n    infer TError,\n    infer TContext\n  >\n    ? ResolvedRouterMutation<\n        TFnData,\n        DefaultTo<TVariables, void>,\n        DefaultTo<TError, CompatibleError>,\n        TContext\n      >\n    : TConfig[K] extends RouterInfiniteQuery<\n        infer TFnData,\n        infer TVariables,\n        infer TError,\n        infer TPageParam\n      >\n    ? ResolvedRouterInfiniteQuery<\n        TFnData,\n        DefaultTo<TVariables, void>,\n        DefaultTo<TError, CompatibleError>,\n        DefaultTo<TPageParam, number>\n      >\n    : TConfig[K] extends RouterQuery<\n        infer TFnData,\n        infer TVariables,\n        infer TError\n      >\n    ? ResolvedRouterQuery<\n        TFnData,\n        DefaultTo<TVariables, void>,\n        DefaultTo<TError, CompatibleError>\n      >\n    : TConfig[K] extends RouterConfig\n    ? CreateRouter<TConfig[K]>\n    : never\n} & { getKey: () => QueryKey }\n"
  },
  {
    "path": "src/utils.ts",
    "content": "import * as TanstackReactQuery from '@tanstack/react-query'\nimport type { Query, QueryClient, QueryKey } from '@tanstack/react-query'\n\nimport type { Middleware } from './types'\n\nexport const ReactQuery = TanstackReactQuery\n\nexport const isV5 = !!ReactQuery.useSuspenseQuery\n\nexport const suspenseOptions = {\n  enabled: true,\n  suspense: true,\n  keepPreviousData: undefined,\n  useErrorBoundary: (_error: unknown, query: Query) =>\n    query.state.data === undefined,\n}\n\nexport const withMiddleware = (\n  hook: any,\n  defaultOptions: any,\n  type: 'queries' | 'mutations'\n) => {\n  return function useMiddleware(\n    options?: { client?: QueryClient; use?: Middleware[] },\n    queryClient?: QueryClient\n  ) {\n    const [uses, opts]: [Middleware[], any] = [\n      ReactQuery.useQueryClient(\n        // @ts-ignore Compatible with ReactQuery v4\n        isV5 ? queryClient : options\n      ).getDefaultOptions()[type],\n      defaultOptions,\n      options,\n    ].reduce(\n      ([u1, o1], { use: u2 = [], ...o2 } = {}) => [\n        [...u1, ...u2],\n        { ...o1, ...o2 },\n      ],\n      [[]]\n    )\n\n    return uses.reduceRight((next, use) => use(next), hook)(opts, queryClient)\n  }\n}\n\nexport const getKey = (queryKey: QueryKey, variables?: any): QueryKey => {\n  return variables === undefined ? queryKey : [...queryKey, variables]\n}\n"
  },
  {
    "path": "tests/createInfiniteQuery.test.tsx",
    "content": "import { createInfiniteQuery } from '../src/createInfiniteQuery'\nimport { omit, uniqueKey } from './utils'\n\ndescribe('createInfiniteQuery', () => {\n  it('should return the correct key', () => {\n    type Response = {\n      projects: { id: string; name: string }[]\n      nextCursor: number\n    }\n    type Variables = { id: number }\n\n    const key = uniqueKey()\n    const variables = { id: 1 }\n    const fetcher = (_variables: Variables): Promise<Response> => {\n      return fetch(`/test`).then(res => res.json())\n    }\n    const initialPageParam = 1\n    const getNextPageParam = (lastPage: Response) => lastPage.nextCursor\n    const useGeneratedQuery = createInfiniteQuery<Response, Variables, Error>({\n      queryKey: key,\n      fetcher,\n      initialPageParam,\n      getNextPageParam,\n    })\n\n    expect(useGeneratedQuery.getKey()).toEqual(key)\n    expect(useGeneratedQuery.getKey(variables)).toEqual([...key, variables])\n    expect(omit(useGeneratedQuery.getOptions(variables), 'queryFn')).toEqual({\n      queryKey: [...key, variables],\n      fetcher,\n      initialPageParam,\n      getNextPageParam,\n    })\n    expect(\n      omit(useGeneratedQuery.getFetchOptions(variables), 'queryFn')\n    ).toEqual({\n      queryKey: [...key, variables],\n      initialPageParam,\n      getNextPageParam,\n    })\n  })\n})\n"
  },
  {
    "path": "tests/createMutation.test.tsx",
    "content": "import { type MutationKey } from '@tanstack/react-query'\n\nimport { createMutation } from '../src/createMutation'\n\ndescribe('createMutation', () => {\n  it('should return the correct key', () => {\n    const mutationKey: MutationKey = ['mutationKey']\n    const mutation = createMutation({\n      mutationKey,\n      mutationFn: async () => mutationKey,\n    })\n\n    expect(mutation.getKey()).toEqual(mutationKey)\n    mutation.mutationFn().then(data => expect(data).toEqual(mutationKey))\n  })\n})\n"
  },
  {
    "path": "tests/createQuery.test.tsx",
    "content": "import { QueryClient, skipToken } from '@tanstack/react-query'\nimport '@testing-library/jest-dom'\nimport { fireEvent, waitFor } from '@testing-library/react'\nimport * as React from 'react'\n\nimport { createQuery } from '../src'\nimport type { QueryHookResult } from '../src'\nimport { Middleware } from '../src/types'\nimport { omit, renderWithClient, sleep, uniqueKey } from './utils'\n\ndescribe('createQuery', () => {\n  const queryClient = new QueryClient()\n\n  it('should return the correct key', () => {\n    const key = uniqueKey()\n    const variables = { id: 1 }\n    const fetcher = (_variables: { id: number }) => {\n      return 'test'\n    }\n    const useGeneratedQuery = createQuery({\n      queryKey: key,\n      fetcher,\n    })\n\n    expect(useGeneratedQuery.getKey()).toEqual(key)\n    expect(useGeneratedQuery.getKey(variables)).toEqual([...key, variables])\n    expect(omit(useGeneratedQuery.getOptions(variables), 'queryFn')).toEqual({\n      queryKey: [...key, variables],\n      fetcher,\n    })\n    expect(\n      omit(useGeneratedQuery.getFetchOptions(variables), 'queryFn')\n    ).toEqual({\n      queryKey: [...key, variables],\n    })\n\n    queryClient.prefetchQuery(useGeneratedQuery.getFetchOptions(variables))\n  })\n\n  it('should return the correct initial data from middleware', async () => {\n    const myMiddileware: Middleware = useQueryNext => {\n      return options => {\n        return useQueryNext({\n          ...options,\n          initialData: 'initialData',\n          enabled: false,\n        })\n      }\n    }\n\n    const useGeneratedQuery = createQuery({\n      queryKey: uniqueKey(),\n      fetcher: (_variables: { id: number }) => {\n        return 'test'\n      },\n      use: [\n        useNext => {\n          return options =>\n            useNext({\n              ...options,\n              initialData: 'fakeData',\n              enabled: false,\n            })\n        },\n        myMiddileware,\n      ],\n    })\n\n    const states: QueryHookResult<any, any>[] = []\n\n    function Page() {\n      const state = useGeneratedQuery()\n\n      states.push(state)\n\n      return <span>{state.data}</span>\n    }\n\n    const rendered = renderWithClient(queryClient, <Page />)\n\n    await waitFor(() => rendered.getByText('initialData'))\n  })\n\n  it('should return the correct initial data', async () => {\n    const useGeneratedQuery = createQuery<string, { id: number }>({\n      queryKey: uniqueKey(),\n      fetcher: () => {\n        return 'test'\n      },\n      use: [\n        useNext => {\n          return options =>\n            useNext({\n              ...options,\n              initialData: options.initialData ?? 'initialData',\n              enabled: false,\n            })\n        },\n      ],\n    })\n    const states: QueryHookResult<any, any>[] = []\n\n    function Page() {\n      const state = useGeneratedQuery({ initialData: 'stateData' })\n\n      states.push(state)\n\n      return <span>{state.data}</span>\n    }\n\n    const rendered = renderWithClient(queryClient, <Page />)\n\n    await waitFor(() => rendered.getByText('stateData'))\n  })\n\n  it('should return the selected data', async () => {\n    const useGeneratedQuery = createQuery<string>({\n      queryKey: uniqueKey(),\n      fetcher: () => {\n        return 'test'\n      },\n    })\n    const states: QueryHookResult<any, any>[] = []\n\n    function Page() {\n      const state = useGeneratedQuery({\n        select() {\n          return 'selectedData'\n        },\n      })\n\n      states.push(state)\n\n      return <span>{state.data}</span>\n    }\n\n    const rendered = renderWithClient(queryClient, <Page />)\n\n    await waitFor(() => rendered.getByText('selectedData'))\n  })\n\n  it('should respect skipToken and refetch when skipToken is taken away', async () => {\n    const useGeneratedQuery = createQuery<string>({\n      queryKey: uniqueKey(),\n      fetcher: async () => {\n        await sleep(10)\n        return Promise.resolve('data')\n      },\n    })\n\n    function Page({ enabled }: { enabled: boolean }) {\n      const { data, status } = useGeneratedQuery({\n        variables: enabled ? undefined : skipToken,\n        retry: false,\n        retryOnMount: false,\n        refetchOnMount: false,\n        refetchOnWindowFocus: false,\n      })\n\n      return (\n        <div>\n          <div>status: {status}</div>\n          <div>data: {String(data)}</div>\n        </div>\n      )\n    }\n\n    function App() {\n      const [enabled, toggle] = React.useReducer(x => !x, false)\n\n      return (\n        <div>\n          <Page enabled={enabled} />\n          <button onClick={toggle}>enable</button>\n        </div>\n      )\n    }\n\n    const rendered = renderWithClient(queryClient, <App />)\n\n    await waitFor(() => rendered.getByText('status: pending'))\n\n    fireEvent.click(rendered.getByRole('button', { name: 'enable' }))\n    await waitFor(() => rendered.getByText('status: success'))\n    await waitFor(() => rendered.getByText('data: data'))\n  })\n})\n"
  },
  {
    "path": "tests/router.test.tsx",
    "content": "import { router } from '../src'\n\ndescribe('router', () => {\n  it('should return the correct shape', () => {\n    const post = router(`post`, {\n      byId: router.query({\n        fetcher: (variables: {\n          id: number\n        }): Promise<{ title: string; content: string }> =>\n          fetch(`/post/${variables.id}`).then(res => res.json()),\n      }),\n\n      list: router.infiniteQuery({\n        fetcher: (_variables, { pageParam }) =>\n          fetch(`/post/?cursor=${pageParam}`).then(res => res.json()),\n        getNextPageParam: lastPage => lastPage.nextCursor,\n        initialPageParam: 0,\n      }),\n\n      add: router.mutation({\n        mutationFn: async (variables: {\n          title: string\n          content: string\n        }): Promise<{\n          ret: number\n        }> =>\n          fetch('/post', {\n            method: 'POST',\n            body: JSON.stringify(variables),\n          }).then(res => res.json()),\n      }),\n\n      command: {\n        report: router.query({\n          fetcher: (variables: {\n            id: number\n          }): Promise<{ title: string; content: string }> =>\n            fetch(`/post/report/${variables.id}`).then(res => res.json()),\n        }),\n      },\n    })\n\n    expect(post.getKey()).toEqual(['post'])\n    expect(post.byId.getKey()).toEqual(['post', 'byId'])\n    expect(post.byId.getKey({ id: 1 })).toEqual(['post', 'byId', { id: 1 }])\n    expect(post.list.getKey()).toEqual(['post', 'list'])\n    expect(post.add.getKey()).toEqual(['post', 'add'])\n    expect(post.command.getKey()).toEqual(['post', 'command'])\n    expect(post.command.report.getKey()).toEqual(['post', 'command', 'report'])\n    expect(post.command.report.getKey({ id: 1 })).toEqual([\n      'post',\n      'command',\n      'report',\n      { id: 1 },\n    ])\n    expect(typeof post.command.report.fetcher === 'function').toBe(true)\n    expect(typeof post.command.report.getFetchOptions === 'function').toBe(true)\n    expect(typeof post.command.report.getOptions === 'function').toBe(true)\n    expect(typeof post.command.report.useQuery === 'function').toBe(true)\n    expect(typeof post.command.report.useSuspenseQuery === 'function').toBe(\n      true\n    )\n    expect(typeof post.byId.fetcher === 'function').toBe(true)\n    expect(typeof post.byId.getFetchOptions === 'function').toBe(true)\n    expect(typeof post.byId.getOptions === 'function').toBe(true)\n    expect(typeof post.byId.useQuery === 'function').toBe(true)\n    expect(typeof post.byId.useSuspenseQuery === 'function').toBe(true)\n    expect(typeof post.list.fetcher === 'function').toBe(true)\n    expect(typeof post.list.getFetchOptions === 'function').toBe(true)\n    expect(typeof post.list.getOptions === 'function').toBe(true)\n    expect(typeof post.list.useInfiniteQuery === 'function').toBe(true)\n    expect(typeof post.list.useSuspenseInfiniteQuery === 'function').toBe(true)\n    expect(typeof post.add.mutationFn === 'function').toBe(true)\n    expect(typeof post.add.getKey === 'function').toBe(true)\n    expect(typeof post.add.getOptions === 'function').toBe(true)\n    expect(typeof post.add.useMutation === 'function').toBe(true)\n  })\n\n  it('should return the correct shape when pass a array keys', () => {\n    const post = router(['scope', `post`], {\n      byId: router.query({\n        fetcher: (variables: {\n          id: number\n        }): Promise<{ title: string; content: string }> =>\n          fetch(`/post/${variables.id}`).then(res => res.json()),\n      }),\n\n      list: router.infiniteQuery({\n        fetcher: (_variables, { pageParam }) =>\n          fetch(`/post/?cursor=${pageParam}`).then(res => res.json()),\n        getNextPageParam: lastPage => lastPage.nextCursor,\n        initialPageParam: 0,\n      }),\n\n      add: router.mutation({\n        mutationFn: async (variables: {\n          title: string\n          content: string\n        }): Promise<{\n          ret: number\n        }> =>\n          fetch('/post', {\n            method: 'POST',\n            body: JSON.stringify(variables),\n          }).then(res => res.json()),\n      }),\n\n      command: {\n        report: router.query({\n          fetcher: (variables: {\n            id: number\n          }): Promise<{ title: string; content: string }> =>\n            fetch(`/post/report/${variables.id}`).then(res => res.json()),\n        }),\n      },\n    })\n\n    expect(post.getKey()).toEqual(['scope', 'post'])\n    expect(post.byId.getKey()).toEqual(['scope', 'post', 'byId'])\n    expect(post.byId.getKey({ id: 1 })).toEqual([\n      'scope',\n      'post',\n      'byId',\n      { id: 1 },\n    ])\n    expect(post.list.getKey()).toEqual(['scope', 'post', 'list'])\n    expect(post.add.getKey()).toEqual(['scope', 'post', 'add'])\n    expect(post.command.getKey()).toEqual(['scope', 'post', 'command'])\n    expect(post.command.report.getKey()).toEqual([\n      'scope',\n      'post',\n      'command',\n      'report',\n    ])\n    expect(post.command.report.getKey({ id: 1 })).toEqual([\n      'scope',\n      'post',\n      'command',\n      'report',\n      { id: 1 },\n    ])\n    expect(typeof post.command.report.fetcher === 'function').toBe(true)\n    expect(typeof post.command.report.getFetchOptions === 'function').toBe(true)\n    expect(typeof post.command.report.getOptions === 'function').toBe(true)\n    expect(typeof post.command.report.useQuery === 'function').toBe(true)\n    expect(typeof post.command.report.useSuspenseQuery === 'function').toBe(\n      true\n    )\n    expect(typeof post.byId.fetcher === 'function').toBe(true)\n    expect(typeof post.byId.getFetchOptions === 'function').toBe(true)\n    expect(typeof post.byId.getOptions === 'function').toBe(true)\n    expect(typeof post.byId.useQuery === 'function').toBe(true)\n    expect(typeof post.byId.useSuspenseQuery === 'function').toBe(true)\n    expect(typeof post.list.fetcher === 'function').toBe(true)\n    expect(typeof post.list.getFetchOptions === 'function').toBe(true)\n    expect(typeof post.list.getOptions === 'function').toBe(true)\n    expect(typeof post.list.useInfiniteQuery === 'function').toBe(true)\n    expect(typeof post.list.useSuspenseInfiniteQuery === 'function').toBe(true)\n    expect(typeof post.add.mutationFn === 'function').toBe(true)\n    expect(typeof post.add.getKey === 'function').toBe(true)\n    expect(typeof post.add.getOptions === 'function').toBe(true)\n    expect(typeof post.add.useMutation === 'function').toBe(true)\n  })\n})\n"
  },
  {
    "path": "tests/types.typecheck.ts",
    "content": "import { createQuery, router } from '../src'\nimport type { Middleware } from '../src'\n\nconst usePost = createQuery<{ title: string }, { id: number }>({\n  queryKey: ['post'],\n  fetcher: async variables => ({\n    title: `post-${variables.id}`,\n  }),\n})\n\nconst queryMiddleware: Middleware<typeof usePost> = useQueryNext => options => {\n  options.fetcher\n  options.variables?.id\n\n  // @ts-expect-error Query middleware should not expose mutation options.\n  options.mutationFn\n\n  return useQueryNext(options)\n}\n\nvoid queryMiddleware\n\ntype UsePostOptions = Parameters<typeof usePost>[0]\n\nconst validQueryOptions: UsePostOptions = {\n  variables: { id: 1 },\n}\n\nvoid validQueryOptions\n\nconst invalidQueryOptions: UsePostOptions = {\n  // @ts-expect-error Query hooks should not accept mutation options.\n  mutationFn: async () => ({ title: 'post-1' }),\n}\n\nvoid invalidQueryOptions\n\nconst postRoutes = router('post', {\n  byId: router.query({\n    fetcher: async (variables: { id: number }) => ({\n      id: variables.id,\n      title: `post-${variables.id}`,\n    }),\n  }),\n  add: router.mutation({\n    mutationFn: async (variables: { title: string }) => ({\n      title: variables.title,\n    }),\n  }),\n})\n\npostRoutes.byId.useQuery({\n  variables: { id: 1 },\n  enabled: false,\n})\n\npostRoutes.add.useMutation({\n  onSuccess(data) {\n    const title: string = data.title\n\n    void title\n  },\n})\n\n// @ts-expect-error Query routes should not expose mutation hooks.\npostRoutes.byId.useMutation()\n\n// @ts-expect-error Mutation routes should not expose query hooks.\npostRoutes.add.useQuery()\n"
  },
  {
    "path": "tests/utils.tsx",
    "content": "import { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport { render } from '@testing-library/react'\nimport * as React from 'react'\n\nlet queryKeyCount = 0\nexport function uniqueKey(): string[] {\n  queryKeyCount++\n  return [`query_${queryKeyCount}`]\n}\n\nexport function renderWithClient(\n  client: QueryClient,\n  ui: React.ReactElement\n): ReturnType<typeof render> {\n  const { rerender, ...result } = render(\n    <QueryClientProvider client={client}>{ui}</QueryClientProvider>\n  )\n  return {\n    ...result,\n    rerender: (rerenderUi: React.ReactElement) =>\n      rerender(\n        <QueryClientProvider client={client}>{rerenderUi}</QueryClientProvider>\n      ),\n  } as any\n}\n\nexport function omit<T extends object, K extends string[]>(\n  object: T | null | undefined,\n  ...paths: K\n): Pick<T, Exclude<keyof T, K[number]>> {\n  return Object.fromEntries(\n    Object.entries(object || {}).filter(([key]) => !paths.includes(key))\n  ) as Pick<T, Exclude<keyof T, K[number]>>\n}\n\nexport function sleep(timeout: number): Promise<void> {\n  return new Promise((resolve, _reject) => {\n    setTimeout(resolve, timeout)\n  })\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ES2020\"],\n    \"target\": \"ES2020\",\n    \"module\": \"ES2020\",\n    \"moduleResolution\": \"node\",\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"noImplicitAny\": true,\n    \"noImplicitReturns\": false,\n    \"noImplicitThis\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"strictNullChecks\": true,\n    \"jsx\": \"react\",\n    \"declaration\": true,\n    \"emitDeclarationOnly\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true,\n    \"types\": [\"jest\", \"node\"],\n    \"outDir\": \"./build/lib\"\n  },\n  \"files\": [\"src/index.ts\"],\n  \"include\": [\"src\"]\n}\n"
  },
  {
    "path": "tsconfig.types.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"declaration\": false,\n    \"emitDeclarationOnly\": false,\n    \"noEmit\": true,\n    \"outDir\": \"./build/types\"\n  },\n  \"files\": [\"tests/types.typecheck.ts\"]\n}\n"
  }
]