openapi-typesafe-react-query 1.0.0-rc.1 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/index.cjs +15 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -3
- package/dist/index.d.mts +44 -3
- package/dist/index.d.ts +44 -3
- package/dist/index.mjs +15 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
- package/src/index.ts +90 -24
package/README.md
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
# openapi-typesafe-react-query
|
|
2
2
|
|
|
3
|
-
openapi-typesafe-react-query is a small, type-safe wrapper (about
|
|
3
|
+
openapi-typesafe-react-query is a small, type-safe wrapper (about 1 kB minified) around [@tanstack/react-query](https://tanstack.com/query/latest/docs/framework/react/overview) to work with OpenAPI schema.
|
|
4
4
|
|
|
5
5
|
It works by using [openapi-typesafe-fetch](../openapi-typesafe-fetch) and [openapi-typesafe](../openapi-typesafe) so URLs, parameters, request bodies and responses are checked against your schema, with no manual types,
|
|
6
6
|
no `any` and no `as` assertions.
|
|
7
7
|
|
|
8
8
|
A maintained, drop-in successor to `openapi-react-query` (see the [repository README](../../README.md)).
|
|
9
9
|
|
|
10
|
-
> **
|
|
10
|
+
> **Release candidate.** The current version is a 1.0.0 release candidate (`1.0.0-rc.N`);
|
|
11
|
+
> names and output can still change before 1.0.0.
|
|
11
12
|
|
|
12
13
|
## Setup
|
|
13
14
|
|
|
14
15
|
Install this library along with [openapi-typesafe-fetch](../openapi-typesafe-fetch) and [openapi-typesafe](../openapi-typesafe):
|
|
15
16
|
|
|
16
17
|
```bash
|
|
17
|
-
npm i openapi-typesafe-react-query openapi-typesafe-fetch
|
|
18
|
+
npm i openapi-typesafe-react-query openapi-typesafe-fetch @tanstack/react-query
|
|
18
19
|
npm i -D openapi-typesafe typescript
|
|
19
20
|
```
|
|
20
21
|
|
|
@@ -45,10 +46,10 @@ const MyComponent = () => {
|
|
|
45
46
|
},
|
|
46
47
|
});
|
|
47
48
|
|
|
48
|
-
if (isPending || !data) return "Loading...";
|
|
49
|
-
|
|
50
49
|
if (error) return `An error occurred: ${error.message}`;
|
|
51
50
|
|
|
51
|
+
if (isPending || !data) return "Loading...";
|
|
52
|
+
|
|
52
53
|
return <div>{data.title}</div>;
|
|
53
54
|
};
|
|
54
55
|
```
|
|
@@ -62,6 +63,12 @@ const MyComponent = () => {
|
|
|
62
63
|
+ import createClient from "openapi-typesafe-react-query";
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
## Versioning
|
|
67
|
+
|
|
68
|
+
Semantic versioning from 1.0.0, with one version for all openapi-typesafe packages. What the
|
|
69
|
+
version promises, and what may grow in a minor release, is listed under
|
|
70
|
+
[Versioning](https://github.com/openapi-typesafe/openapi-typesafe/blob/main/docs/compatibility.md#versioning).
|
|
71
|
+
|
|
65
72
|
## License
|
|
66
73
|
|
|
67
74
|
[MIT](./LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -33,8 +33,15 @@ function createClient(client) {
|
|
|
33
33
|
});
|
|
34
34
|
return {
|
|
35
35
|
queryOptions,
|
|
36
|
-
useQuery: (method, path, ...[init, options, queryClient]) => reactQuery.useQuery(
|
|
37
|
-
|
|
36
|
+
useQuery: (method, path, ...[init, options, queryClient]) => reactQuery.useQuery(
|
|
37
|
+
queryOptions(method, path, init, options),
|
|
38
|
+
queryClient
|
|
39
|
+
),
|
|
40
|
+
useSuspenseQuery: (method, path, ...[init, options, queryClient]) => reactQuery.useSuspenseQuery(
|
|
41
|
+
queryOptions(method, path, init, options),
|
|
42
|
+
queryClient
|
|
43
|
+
),
|
|
44
|
+
// biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)
|
|
38
45
|
useInfiniteQuery: (method, path, init, options, queryClient) => {
|
|
39
46
|
const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;
|
|
40
47
|
const { queryKey } = queryOptions(method, path, init);
|
|
@@ -45,13 +52,18 @@ function createClient(client) {
|
|
|
45
52
|
queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER],
|
|
46
53
|
// The request comes from the arguments; the key only identifies the cache entry.
|
|
47
54
|
queryFn: async ({ pageParam, signal }) => {
|
|
48
|
-
const result = await fn(
|
|
55
|
+
const result = await fn(
|
|
56
|
+
path,
|
|
57
|
+
// biome-ignore lint/suspicious/noExplicitAny: a generic init; see queryFn
|
|
58
|
+
pageInit(init, { pageParamName, pageParam, signal })
|
|
59
|
+
);
|
|
49
60
|
throwIfFailed(result);
|
|
50
61
|
return result.data;
|
|
51
62
|
},
|
|
52
63
|
...restOptions,
|
|
53
64
|
// Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack
|
|
54
65
|
// passes the previous data of this query, with its own page params, and takes it back.
|
|
66
|
+
// biome-ignore lint/suspicious/noExplicitAny: TanStack's type carries its own page params
|
|
55
67
|
placeholderData
|
|
56
68
|
},
|
|
57
69
|
queryClient
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import {\n type DataTag,\n type InfiniteData,\n type QueryClient,\n type QueryFunctionContext,\n type SkipToken,\n type UseInfiniteQueryOptions,\n type UseInfiniteQueryResult,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n type UseSuspenseQueryOptions,\n type UseSuspenseQueryResult,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseQuery,\n} from \"@tanstack/react-query\";\nimport type {\n ClientMethod,\n DefaultParamsOption,\n Client as FetchClient,\n FetchResponse,\n IsInitOptional,\n MaybeOptionalInit,\n PathOperation,\n} from \"openapi-typesafe-fetch\";\nimport type { HttpMethod, MediaType, PathsWithMethod } from \"openapi-typesafe-helpers\";\n\n// Helper type to dynamically infer the type from the `select` property\ntype InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;\n\n/**\n * Query data of a response type: a success without a body is `undefined` in the fetch client and\n * `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).\n */\ntype QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;\n\ntype InitWithUnknowns<Init> = Init & { [key: string]: unknown };\n\n/**\n * The arguments after the path: the init, required when the operation requires a parameter or a\n * body, then `Rest`. `IsInitOptional` keeps the init optional in\n * `Parameters<typeof $api.queryOptions>`, where it is the union of all inits (upstream #2178).\n */\ntype InitArguments<Init, Rest extends unknown[]> =\n IsInitOptional<Init> extends true ? [InitWithUnknowns<Init>?, ...Rest] : [InitWithUnknowns<Init>, ...Rest];\n\nexport type QueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init = MaybeOptionalInit<Paths[Path], Method>,\n> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];\n\n/**\n * The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different\n * data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream\n * #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime\n * named export next to the default export would change what `require()` returns.\n */\nconst INFINITE_QUERY_KEY_MARKER = \"infinite\";\n\nexport type InfiniteQueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];\n\nexport type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n path: Path,\n ...[init, options]: InitArguments<Init, [Options?]>\n) => NoInfer<\n Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryFn\" | \"queryKey\"\n > & {\n queryKey: DataTag<QueryKey<Paths, Method, Path>, QueryData<Response[\"data\"]>, Response[\"error\"]>;\n queryFn: Exclude<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >[\"queryFn\"],\n SkipToken | undefined\n >;\n }\n>;\n\nexport type UseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseQueryResult<InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>, Response[\"error\"]>;\n\n/**\n * The type of the page params: the initial one and the ones `getNextPageParam` and\n * `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a\n * return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot\n * infer the return type of a callback that computes it from an unannotated `lastPageParam`,\n * whose type depends on that return type, and gives `never` or `unknown`.\n */\ntype PageParam<Initial, Next, Previous> =\n IsUntyped<Next> extends true\n ? unknown\n : [Previous] extends [NoPreviousPageParam]\n ? NullishToUnknown<Initial | NonNullable<Next>>\n : IsUntyped<Previous> extends true\n ? unknown\n : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;\n\n/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */\ndeclare const NO_PREVIOUS_PAGE_PARAM: unique symbol;\ntype NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;\n\ntype IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;\n\n/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */\ntype NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;\n\n/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */\ntype PageParamFunction<Result> = (...args: never) => Result;\n\n/**\n * Inference sites of the page param types. Intersected before `Options`, so TanStack's own\n * inference in the implementation reads the full signatures of `Options`.\n */\ninterface PageParamSources<Initial, Next, Previous> {\n initialPageParam: Initial;\n getNextPageParam: PageParamFunction<Next>;\n getPreviousPageParam?: PageParamFunction<Previous>;\n}\n\n/**\n * `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types\n * a `(previousData) => previousData` callback while it still infers the page param, so it got\n * `unknown` there and failed against the typed page param.\n */\ntype InfinitePlaceholderData<\n Data,\n Error,\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = UseInfiniteQueryOptions<\n Data,\n Error,\n InfiniteData<Data, unknown>,\n InfiniteQueryKey<Paths, Method, Path>,\n unknown\n>[\"placeholderData\"];\n\nexport type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseInfiniteQueryOptions<\n Response[\"data\"],\n Response[\"error\"],\n InferSelectReturnType<InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>, Options[\"select\"]>,\n InfiniteQueryKey<Paths, Method, Path>,\n PageParam<Initial, Next, Previous>\n >,\n \"queryKey\" | \"queryFn\" | \"placeholderData\"\n > & {\n placeholderData?: InfinitePlaceholderData<Response[\"data\"], Response[\"error\"], Paths, Method, Path>;\n pageParamName?: string;\n },\n Initial = unknown,\n Next = never,\n Previous = NoPreviousPageParam,\n>(\n method: Method,\n url: Path,\n init: InitWithUnknowns<Init>,\n options: PageParamSources<Initial, Next, Previous> & Options,\n queryClient?: QueryClient,\n) => UseInfiniteQueryResult<\n InferSelectReturnType<InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>, Options[\"select\"]>,\n Response[\"error\"]\n>;\n\nexport type UseSuspenseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n Options extends Omit<\n UseSuspenseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseSuspenseQueryResult<InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>, Response[\"error\"]>;\n\nexport type UseMutationMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n TOnMutateResult = unknown,\n>(\n method: Method,\n url: Path,\n options?: Omit<\n UseMutationOptions<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>,\n \"mutationKey\" | \"mutationFn\"\n >,\n queryClient?: QueryClient,\n) => UseMutationResult<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>;\n\nexport interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaType> {\n queryOptions: QueryOptionsFunction<Paths, Media>;\n useQuery: UseQueryMethod<Paths, Media>;\n useSuspenseQuery: UseSuspenseQueryMethod<Paths, Media>;\n useInfiniteQuery: UseInfiniteQueryMethod<Paths, Media>;\n useMutation: UseMutationMethod<Paths, Media>;\n}\n\nexport type MethodResponse<\n CreatedClient extends OpenapiQueryClient<any, any>,\n Method extends HttpMethod,\n Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media>\n ? PathsWithMethod<Paths, Method>\n : never,\n Options = object,\n> =\n CreatedClient extends OpenapiQueryClient<infer Paths extends { [key: string]: any }, infer Media extends MediaType>\n ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>[\"data\"]>\n : never;\n\n/**\n * Throw the error of a failed fetch result. `if (error)` missed two failures: a non-OK response with\n * an empty body (`error` is `undefined`, upstream #2070, #2829) and a falsy error body such as `0`.\n */\nfunction throwIfFailed(result: { error?: unknown; response: Response }): void {\n if (result.error !== undefined || !result.response.ok) {\n throw result.error;\n }\n}\n\n/** The query parameter that carries the page param unless `pageParamName` names another. */\nconst DEFAULT_PAGE_PARAM_NAME = \"cursor\";\n\ninterface PageRequest {\n readonly pageParamName: string;\n readonly pageParam: unknown;\n readonly signal: AbortSignal;\n}\n\n/**\n * The request init of one page. An `undefined` page param (the first page with\n * `initialPageParam: undefined`) is left out instead of being sent as `0` (upstream #2715, #2340,\n * ADR-022), so a cursor API gets no cursor on the first page.\n */\nfunction pageInit(init: unknown, { pageParamName, pageParam, signal }: PageRequest) {\n const { params, ...rest } = (init ?? {}) as { params?: { query?: DefaultParamsOption } };\n const page = pageParam === undefined ? {} : { [pageParamName]: pageParam };\n return { ...rest, signal, params: { ...params, query: { ...params?.query, ...page } } };\n}\n\n// TODO: Add the ability to bring queryClient as argument\nexport default function createClient<Paths extends {}, Media extends MediaType = MediaType>(\n client: FetchClient<Paths, Media>,\n): OpenapiQueryClient<Paths, Media> {\n const queryFn = async <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>>({\n queryKey,\n signal,\n }: QueryFunctionContext<QueryKey<Paths, Method, Path>>) => {\n // While `Path` is generic, TypeScript cannot work out that the key is a tuple: the init is a\n // conditional type per operation (`MaybeOptionalInit`), and the key's type waits for it.\n const [method, path, init] = queryKey as readonly [Method, Path, unknown?];\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n const result = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any\n throwIfFailed(result);\n // TanStack Query rejects `undefined` query data. The fetch client gives `undefined` for every\n // success without a body: a 204, `Content-Length: 0`, an empty chunked body, a HEAD request\n // (upstream #2234). A parsed body is never `undefined`, so this changes nothing else.\n return result.data ?? null;\n };\n\n const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({\n queryKey: (init === undefined ? ([method, path] as const) : ([method, path, init] as const)) as DataTag<\n QueryKey<Paths, typeof method, typeof path>,\n any,\n any\n >,\n queryFn,\n ...options,\n });\n\n return {\n queryOptions,\n useQuery: (method, path, ...[init, options, queryClient]) =>\n useQuery(queryOptions(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),\n useSuspenseQuery: (method, path, ...[init, options, queryClient]) =>\n useSuspenseQuery(queryOptions(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),\n useInfiniteQuery: (method, path, init, options, queryClient) => {\n const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;\n const { queryKey } = queryOptions(method, path, init);\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n return useInfiniteQuery(\n {\n queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER] as const,\n // The request comes from the arguments; the key only identifies the cache entry.\n queryFn: async ({ pageParam, signal }) => {\n const result = await fn(path, pageInit(init, { pageParamName, pageParam, signal }) as any);\n throwIfFailed(result);\n // An empty page stays `undefined`, as in upstream: TanStack Query rejects only\n // `undefined` query data, and the infinite query data is the `{ pages }` object.\n return result.data;\n },\n ...restOptions,\n // Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack\n // passes the previous data of this query, with its own page params, and takes it back.\n placeholderData: placeholderData as any,\n },\n queryClient,\n );\n },\n useMutation: (method, path, options, queryClient) =>\n useMutation(\n {\n mutationKey: [method, path],\n mutationFn: async (init) => {\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n const result = await fn(path, init as InitWithUnknowns<typeof init>);\n throwIfFailed(result);\n // TanStack Query accepts `undefined` mutation data, so a success without a body stays\n // `undefined`, as in upstream; only query data needs `null` (upstream #2234). The cast\n // only bridges the generic types: `Response[\"data\"]` still has `undefined` for an\n // operation with a response without a body.\n const { data } = result;\n return data as Exclude<typeof data, undefined>;\n },\n ...options,\n },\n queryClient,\n ),\n };\n}\n"],"names":["useQuery","useSuspenseQuery","useInfiniteQuery","useMutation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import {\n type DataTag,\n type InfiniteData,\n type QueryClient,\n type QueryFunctionContext,\n type SkipToken,\n type UseInfiniteQueryOptions,\n type UseInfiniteQueryResult,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n type UseSuspenseQueryOptions,\n type UseSuspenseQueryResult,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseQuery,\n} from \"@tanstack/react-query\";\nimport type {\n ClientMethod,\n DefaultParamsOption,\n Client as FetchClient,\n FetchResponse,\n MaybeOptionalInit,\n PathOperation,\n} from \"openapi-typesafe-fetch\";\nimport type {\n HttpMethod,\n MediaType,\n PathsWithMethod,\n RequiredKeysOf,\n} from \"openapi-typesafe-helpers\";\n\n// Helper type to dynamically infer the type from the `select` property\ntype InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;\n\n/**\n * Query data of a response type: a success without a body is `undefined` in the fetch client and\n * `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).\n */\ntype QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;\n\ntype InitWithUnknowns<Init> = Init & { [key: string]: unknown };\n\n/**\n * The arguments after the path: the init, required when the operation requires a parameter or a\n * body, then `Rest`. The condition is openapi-react-query's, as `InitParam` of the fetch client\n * is openapi-fetch's (ADR-030, decision 2): TypeScript relates two conditional types only when\n * their `extends` types are the same, so a hook type copied from openapi-react-query takes these\n * hooks. For a union of inits, `MaybeOptionalInit` leaves out `undefined` when one is required,\n * so `Parameters<typeof $api.queryOptions>` still requires it then (upstream #2178).\n */\ntype InitArguments<Init, Rest extends unknown[]> =\n RequiredKeysOf<Init> extends never\n ? [InitWithUnknowns<Init>?, ...Rest]\n : [InitWithUnknowns<Init>, ...Rest];\n\nexport type QueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init = MaybeOptionalInit<Paths[Path], Method>,\n> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];\n\n/**\n * The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different\n * data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream\n * #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime\n * named export next to the default export would change what `require()` returns.\n */\nconst INFINITE_QUERY_KEY_MARKER = \"infinite\";\n\nexport type InfiniteQueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];\n\nexport type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n path: Path,\n ...[init, options]: InitArguments<Init, [Options?]>\n) => NoInfer<\n Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryFn\" | \"queryKey\"\n > & {\n queryKey: DataTag<\n QueryKey<Paths, Method, Path>,\n QueryData<Response[\"data\"]>,\n Response[\"error\"]\n >;\n queryFn: Exclude<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >[\"queryFn\"],\n SkipToken | undefined\n >;\n }\n>;\n\nexport type UseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseQueryResult<\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n Response[\"error\"]\n>;\n\n/**\n * The type of the page params: the initial one and the ones `getNextPageParam` and\n * `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a\n * return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot\n * infer the return type of a callback that computes it from an unannotated `lastPageParam`,\n * whose type depends on that return type, and gives `never` or `unknown`.\n */\ntype PageParam<Initial, Next, Previous> =\n IsUntyped<Next> extends true\n ? unknown\n : [Previous] extends [NoPreviousPageParam]\n ? NullishToUnknown<Initial | NonNullable<Next>>\n : IsUntyped<Previous> extends true\n ? unknown\n : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;\n\n/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */\ndeclare const NO_PREVIOUS_PAGE_PARAM: unique symbol;\ntype NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;\n\ntype IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;\n\n/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */\ntype NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;\n\n/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */\ntype PageParamFunction<Result> = (...args: never) => Result;\n\n/**\n * Inference sites of the page param types. Intersected before `Options`, so TanStack's own\n * inference in the implementation reads the full signatures of `Options`.\n */\ninterface PageParamSources<Initial, Next, Previous> {\n initialPageParam: Initial;\n getNextPageParam: PageParamFunction<Next>;\n getPreviousPageParam?: PageParamFunction<Previous>;\n}\n\n/**\n * `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types\n * a `(previousData) => previousData` callback while it still infers the page param, so it got\n * `unknown` there and failed against the typed page param.\n */\ntype InfinitePlaceholderData<\n Data,\n Error,\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = UseInfiniteQueryOptions<\n Data,\n Error,\n InfiniteData<Data, unknown>,\n InfiniteQueryKey<Paths, Method, Path>,\n unknown\n>[\"placeholderData\"];\n\nexport type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseInfiniteQueryOptions<\n Response[\"data\"],\n Response[\"error\"],\n InferSelectReturnType<\n InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>,\n Options[\"select\"]\n >,\n InfiniteQueryKey<Paths, Method, Path>,\n PageParam<Initial, Next, Previous>\n >,\n \"queryKey\" | \"queryFn\" | \"placeholderData\"\n > & {\n placeholderData?: InfinitePlaceholderData<\n Response[\"data\"],\n Response[\"error\"],\n Paths,\n Method,\n Path\n >;\n pageParamName?: string;\n },\n Initial = unknown,\n Next = never,\n Previous = NoPreviousPageParam,\n // biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)\n>(\n method: Method,\n url: Path,\n init: InitWithUnknowns<Init>,\n options: PageParamSources<Initial, Next, Previous> & Options,\n queryClient?: QueryClient,\n) => UseInfiniteQueryResult<\n InferSelectReturnType<\n InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>,\n Options[\"select\"]\n >,\n Response[\"error\"]\n>;\n\nexport type UseSuspenseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseSuspenseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseSuspenseQueryResult<\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n Response[\"error\"]\n>;\n\nexport type UseMutationMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n TOnMutateResult = unknown,\n>(\n method: Method,\n url: Path,\n options?: Omit<\n UseMutationOptions<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>,\n \"mutationKey\" | \"mutationFn\"\n >,\n queryClient?: QueryClient,\n) => UseMutationResult<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>;\n\nexport interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaType> {\n queryOptions: QueryOptionsFunction<Paths, Media>;\n useQuery: UseQueryMethod<Paths, Media>;\n useSuspenseQuery: UseSuspenseQueryMethod<Paths, Media>;\n useInfiniteQuery: UseInfiniteQueryMethod<Paths, Media>;\n useMutation: UseMutationMethod<Paths, Media>;\n}\n\nexport type MethodResponse<\n // biome-ignore lint/suspicious/noExplicitAny: matches a client of any paths, as upstream's\n CreatedClient extends OpenapiQueryClient<any, any>,\n Method extends HttpMethod,\n Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media>\n ? PathsWithMethod<Paths, Method>\n : never,\n Options = object,\n> =\n CreatedClient extends OpenapiQueryClient<\n // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces\n infer Paths extends { [key: string]: any },\n infer Media extends MediaType\n >\n ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>[\"data\"]>\n : never;\n\n/**\n * Throw the error of a failed fetch result. `if (error)` missed two failures: a non-OK response\n * with an empty body (`error` is `undefined`, upstream #2070, #2829) and a falsy error body such as\n * `0`.\n */\nfunction throwIfFailed(result: { error?: unknown; response: Response }): void {\n if (result.error !== undefined || !result.response.ok) {\n throw result.error;\n }\n}\n\n/** The query parameter that carries the page param unless `pageParamName` names another. */\nconst DEFAULT_PAGE_PARAM_NAME = \"cursor\";\n\ninterface PageRequest {\n readonly pageParamName: string;\n readonly pageParam: unknown;\n readonly signal: AbortSignal;\n}\n\n/**\n * The request init of one page. An `undefined` page param (the first page with\n * `initialPageParam: undefined`) is left out instead of being sent as `0` (upstream #2715, #2340,\n * ADR-022), so a cursor API gets no cursor on the first page.\n */\nfunction pageInit(init: unknown, { pageParamName, pageParam, signal }: PageRequest) {\n const { params, ...rest } = (init ?? {}) as { params?: { query?: DefaultParamsOption } };\n const page = pageParam === undefined ? {} : { [pageParamName]: pageParam };\n return { ...rest, signal, params: { ...params, query: { ...params?.query, ...page } } };\n}\n\n/**\n * Hooks and query options for a fetch client. A `QueryClient` other than the one from the React\n * context is passed to each hook as its last argument; `createClient` does not take one.\n */\n// biome-ignore lint/complexity/noExcessiveLinesPerFunction: inherited, kept until 1.1 (ADR-036)\nexport default function createClient<Paths extends {}, Media extends MediaType = MediaType>(\n client: FetchClient<Paths, Media>,\n): OpenapiQueryClient<Paths, Media> {\n const queryFn = async <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>>({\n queryKey,\n signal,\n }: QueryFunctionContext<QueryKey<Paths, Method, Path>>) => {\n // While `Path` is generic, TypeScript cannot work out that the key is a tuple: the init is a\n // conditional type per operation (`MaybeOptionalInit`), and the key's type waits for it.\n const [method, path, init] = queryKey as readonly [Method, Path, unknown?];\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n // The init is `unknown` here. An `any` init lets the call infer the generic response type\n // that `queryOptions` declares; a typed init makes TypeScript compare two unresolved\n // conditional types, which it cannot do.\n // biome-ignore lint/suspicious/noExplicitAny: see the comment above\n const result = await fn(path, { signal, ...(init as any) });\n throwIfFailed(result);\n // TanStack Query rejects `undefined` query data. The fetch client gives `undefined` for every\n // success without a body: a 204, `Content-Length: 0`, an empty chunked body, a HEAD request\n // (upstream #2234). A parsed body is never `undefined`, so this changes nothing else.\n return result.data ?? null;\n };\n\n const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({\n queryKey: (init === undefined\n ? ([method, path] as const)\n : ([method, path, init] as const)) as DataTag<\n QueryKey<Paths, typeof method, typeof path>,\n // biome-ignore lint/suspicious/noExplicitAny: the declared return type sets the data type\n any,\n // biome-ignore lint/suspicious/noExplicitAny: the declared return type sets the error type\n any\n >,\n queryFn,\n ...options,\n });\n\n return {\n queryOptions,\n useQuery: (method, path, ...[init, options, queryClient]) =>\n useQuery(\n queryOptions(method, path, init as InitWithUnknowns<typeof init>, options),\n queryClient,\n ),\n useSuspenseQuery: (method, path, ...[init, options, queryClient]) =>\n useSuspenseQuery(\n queryOptions(method, path, init as InitWithUnknowns<typeof init>, options),\n queryClient,\n ),\n // biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)\n useInfiniteQuery: (method, path, init, options, queryClient) => {\n const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;\n const { queryKey } = queryOptions(method, path, init);\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n return useInfiniteQuery(\n {\n queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER] as const,\n // The request comes from the arguments; the key only identifies the cache entry.\n queryFn: async ({ pageParam, signal }) => {\n const result = await fn(\n path,\n // biome-ignore lint/suspicious/noExplicitAny: a generic init; see queryFn\n pageInit(init, { pageParamName, pageParam, signal }) as any,\n );\n throwIfFailed(result);\n // An empty page stays `undefined`, as in upstream: TanStack Query rejects only\n // `undefined` query data, and the infinite query data is the `{ pages }` object.\n return result.data;\n },\n ...restOptions,\n // Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack\n // passes the previous data of this query, with its own page params, and takes it back.\n // biome-ignore lint/suspicious/noExplicitAny: TanStack's type carries its own page params\n placeholderData: placeholderData as any,\n },\n queryClient,\n );\n },\n useMutation: (method, path, options, queryClient) =>\n useMutation(\n {\n mutationKey: [method, path],\n mutationFn: async (init) => {\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n const result = await fn(path, init as InitWithUnknowns<typeof init>);\n throwIfFailed(result);\n // TanStack Query accepts `undefined` mutation data, so a success without a body stays\n // `undefined`, as in upstream; only query data needs `null` (upstream #2234). The cast\n // only bridges the generic types: `Response[\"data\"]` still has `undefined` for an\n // operation with a response without a body.\n const { data } = result;\n return data as Exclude<typeof data, undefined>;\n },\n ...options,\n },\n queryClient,\n ),\n };\n}\n"],"names":["useQuery","useSuspenseQuery","useInfiniteQuery","useMutation"],"mappings":";;;;AAuEA,MAAM,yBAAA,GAA4B,UAAA;AA2PlC,SAAS,cAAc,MAAA,EAAuD;AAC5E,EAAA,IAAI,OAAO,KAAA,KAAU,MAAA,IAAa,CAAC,MAAA,CAAO,SAAS,EAAA,EAAI;AACrD,IAAA,MAAM,MAAA,CAAO,KAAA;AAAA,EACf;AACF;AAGA,MAAM,uBAAA,GAA0B,QAAA;AAahC,SAAS,SAAS,IAAA,EAAe,EAAE,aAAA,EAAe,SAAA,EAAW,QAAO,EAAgB;AAClF,EAAA,MAAM,EAAE,MAAA,EAAQ,GAAG,IAAA,EAAK,GAAK,QAAQ,EAAC;AACtC,EAAA,MAAM,IAAA,GAAO,cAAc,MAAA,GAAY,KAAK,EAAE,CAAC,aAAa,GAAG,SAAA,EAAU;AACzE,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,MAAA,EAAQ,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,GAAG,IAAA,IAAO,EAAE;AACxF;AAOA,SAAwB,aACtB,MAAA,EACkC;AAClC,EAAA,MAAM,UAAU,OAA+E;AAAA,IAC7F,QAAA;AAAA,IACA;AAAA,GACF,KAA2D;AAGzD,IAAA,MAAM,CAAC,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA,GAAI,QAAA;AAC7B,IAAA,MAAM,GAAA,GAAM,OAAO,WAAA,EAAY;AAC/B,IAAA,MAAM,EAAA,GAAK,OAAO,GAAG,CAAA;AAKrB,IAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,IAAA,EAAM,EAAE,MAAA,EAAQ,GAAI,MAAc,CAAA;AAC1D,IAAA,aAAA,CAAc,MAAM,CAAA;AAIpB,IAAA,OAAO,OAAO,IAAA,IAAQ,IAAA;AAAA,EACxB,CAAA;AAEA,EAAA,MAAM,eAAmD,CAAC,MAAA,EAAQ,SAAS,CAAC,IAAA,EAAM,OAAO,CAAA,MAAO;AAAA,IAC9F,QAAA,EAAW,IAAA,KAAS,MAAA,GACf,CAAC,MAAA,EAAQ,IAAI,CAAA,GACb,CAAC,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,IAOxB,OAAA;AAAA,IACA,GAAG;AAAA,GACL,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,YAAA;AAAA,IACA,QAAA,EAAU,CAAC,MAAA,EAAQ,IAAA,EAAA,GAAS,CAAC,IAAA,EAAM,OAAA,EAAS,WAAW,CAAA,KACrDA,mBAAA;AAAA,MACE,YAAA,CAAa,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAuC,OAAO,CAAA;AAAA,MACzE;AAAA,KACF;AAAA,IACF,gBAAA,EAAkB,CAAC,MAAA,EAAQ,IAAA,EAAA,GAAS,CAAC,IAAA,EAAM,OAAA,EAAS,WAAW,CAAA,KAC7DC,2BAAA;AAAA,MACE,YAAA,CAAa,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAuC,OAAO,CAAA;AAAA,MACzE;AAAA,KACF;AAAA;AAAA,IAEF,kBAAkB,CAAC,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,SAAS,WAAA,KAAgB;AAC9D,MAAA,MAAM,EAAE,aAAA,GAAgB,uBAAA,EAAyB,eAAA,EAAiB,GAAG,aAAY,GAAI,OAAA;AACrF,MAAA,MAAM,EAAE,QAAA,EAAS,GAAI,YAAA,CAAa,MAAA,EAAQ,MAAM,IAAI,CAAA;AACpD,MAAA,MAAM,GAAA,GAAM,OAAO,WAAA,EAAY;AAC/B,MAAA,MAAM,EAAA,GAAK,OAAO,GAAG,CAAA;AACrB,MAAA,OAAOC,2BAAA;AAAA,QACL;AAAA,UACE,QAAA,EAAU,CAAC,GAAG,QAAA,EAAU,yBAAyB,CAAA;AAAA;AAAA,UAEjD,OAAA,EAAS,OAAO,EAAE,SAAA,EAAW,QAAO,KAAM;AACxC,YAAA,MAAM,SAAS,MAAM,EAAA;AAAA,cACnB,IAAA;AAAA;AAAA,cAEA,SAAS,IAAA,EAAM,EAAE,aAAA,EAAe,SAAA,EAAW,QAAQ;AAAA,aACrD;AACA,YAAA,aAAA,CAAc,MAAM,CAAA;AAGpB,YAAA,OAAO,MAAA,CAAO,IAAA;AAAA,UAChB,CAAA;AAAA,UACA,GAAG,WAAA;AAAA;AAAA;AAAA;AAAA,UAIH;AAAA,SACF;AAAA,QACA;AAAA,OACF;AAAA,IACF,CAAA;AAAA,IACA,WAAA,EAAa,CAAC,MAAA,EAAQ,IAAA,EAAM,SAAS,WAAA,KACnCC,sBAAA;AAAA,MACE;AAAA,QACE,WAAA,EAAa,CAAC,MAAA,EAAQ,IAAI,CAAA;AAAA,QAC1B,UAAA,EAAY,OAAO,IAAA,KAAS;AAC1B,UAAA,MAAM,GAAA,GAAM,OAAO,WAAA,EAAY;AAC/B,UAAA,MAAM,EAAA,GAAK,OAAO,GAAG,CAAA;AACrB,UAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,IAAA,EAAM,IAAqC,CAAA;AACnE,UAAA,aAAA,CAAc,MAAM,CAAA;AAKpB,UAAA,MAAM,EAAE,MAAK,GAAI,MAAA;AACjB,UAAA,OAAO,IAAA;AAAA,QACT,CAAA;AAAA,QACA,GAAG;AAAA,OACL;AAAA,MACA;AAAA;AACF,GACJ;AACF;;"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { UseQueryOptions, DataTag, SkipToken, QueryClient, UseQueryResult, UseSuspenseQueryOptions, UseSuspenseQueryResult, UseInfiniteQueryOptions, InfiniteData, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
|
|
2
|
-
import { MaybeOptionalInit, FetchResponse, PathOperation,
|
|
3
|
-
import { HttpMethod, PathsWithMethod, MediaType } from 'openapi-typesafe-helpers';
|
|
2
|
+
import { MaybeOptionalInit, FetchResponse, PathOperation, Client } from 'openapi-typesafe-fetch';
|
|
3
|
+
import { HttpMethod, PathsWithMethod, MediaType, RequiredKeysOf } from 'openapi-typesafe-helpers';
|
|
4
4
|
|
|
5
5
|
type InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;
|
|
6
|
+
/**
|
|
7
|
+
* Query data of a response type: a success without a body is `undefined` in the fetch client and
|
|
8
|
+
* `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).
|
|
9
|
+
*/
|
|
6
10
|
type QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;
|
|
7
11
|
type InitWithUnknowns<Init> = Init & {
|
|
8
12
|
[key: string]: unknown;
|
|
9
13
|
};
|
|
10
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The arguments after the path: the init, required when the operation requires a parameter or a
|
|
16
|
+
* body, then `Rest`. The condition is openapi-react-query's, as `InitParam` of the fetch client
|
|
17
|
+
* is openapi-fetch's (ADR-030, decision 2): TypeScript relates two conditional types only when
|
|
18
|
+
* their `extends` types are the same, so a hook type copied from openapi-react-query takes these
|
|
19
|
+
* hooks. For a union of inits, `MaybeOptionalInit` leaves out `undefined` when one is required,
|
|
20
|
+
* so `Parameters<typeof $api.queryOptions>` still requires it then (upstream #2178).
|
|
21
|
+
*/
|
|
22
|
+
type InitArguments<Init, Rest extends unknown[]> = RequiredKeysOf<Init> extends never ? [InitWithUnknowns<Init>?, ...Rest] : [InitWithUnknowns<Init>, ...Rest];
|
|
11
23
|
type QueryKey<Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init = MaybeOptionalInit<Paths[Path], Method>> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];
|
|
24
|
+
/**
|
|
25
|
+
* The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different
|
|
26
|
+
* data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream
|
|
27
|
+
* #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime
|
|
28
|
+
* named export next to the default export would change what `require()` returns.
|
|
29
|
+
*/
|
|
12
30
|
declare const INFINITE_QUERY_KEY_MARKER = "infinite";
|
|
13
31
|
type InfiniteQueryKey<Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];
|
|
14
32
|
type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryKey" | "queryFn">>(method: Method, path: Path, ...[init, options]: InitArguments<Init, [Options?]>) => NoInfer<Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryFn" | "queryKey"> & {
|
|
@@ -16,17 +34,36 @@ type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <Method e
|
|
|
16
34
|
queryFn: Exclude<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>["queryFn"], SkipToken | undefined>;
|
|
17
35
|
}>;
|
|
18
36
|
type UseQueryMethod<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryKey" | "queryFn">>(method: Method, url: Path, ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>) => UseQueryResult<InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, Response["error"]>;
|
|
37
|
+
/**
|
|
38
|
+
* The type of the page params: the initial one and the ones `getNextPageParam` and
|
|
39
|
+
* `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a
|
|
40
|
+
* return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot
|
|
41
|
+
* infer the return type of a callback that computes it from an unannotated `lastPageParam`,
|
|
42
|
+
* whose type depends on that return type, and gives `never` or `unknown`.
|
|
43
|
+
*/
|
|
19
44
|
type PageParam<Initial, Next, Previous> = IsUntyped<Next> extends true ? unknown : [Previous] extends [NoPreviousPageParam] ? NullishToUnknown<Initial | NonNullable<Next>> : IsUntyped<Previous> extends true ? unknown : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;
|
|
45
|
+
/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */
|
|
20
46
|
declare const NO_PREVIOUS_PAGE_PARAM: unique symbol;
|
|
21
47
|
type NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;
|
|
22
48
|
type IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;
|
|
49
|
+
/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */
|
|
23
50
|
type NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;
|
|
51
|
+
/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */
|
|
24
52
|
type PageParamFunction<Result> = (...args: never) => Result;
|
|
53
|
+
/**
|
|
54
|
+
* Inference sites of the page param types. Intersected before `Options`, so TanStack's own
|
|
55
|
+
* inference in the implementation reads the full signatures of `Options`.
|
|
56
|
+
*/
|
|
25
57
|
interface PageParamSources<Initial, Next, Previous> {
|
|
26
58
|
initialPageParam: Initial;
|
|
27
59
|
getNextPageParam: PageParamFunction<Next>;
|
|
28
60
|
getPreviousPageParam?: PageParamFunction<Previous>;
|
|
29
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types
|
|
64
|
+
* a `(previousData) => previousData` callback while it still infers the page param, so it got
|
|
65
|
+
* `unknown` there and failed against the typed page param.
|
|
66
|
+
*/
|
|
30
67
|
type InfinitePlaceholderData<Data, Error, Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>> = UseInfiniteQueryOptions<Data, Error, InfiniteData<Data, unknown>, InfiniteQueryKey<Paths, Method, Path>, unknown>["placeholderData"];
|
|
31
68
|
type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseInfiniteQueryOptions<Response["data"], Response["error"], InferSelectReturnType<InfiniteData<Response["data"], PageParam<Initial, Next, Previous>>, Options["select"]>, InfiniteQueryKey<Paths, Method, Path>, PageParam<Initial, Next, Previous>>, "queryKey" | "queryFn" | "placeholderData"> & {
|
|
32
69
|
placeholderData?: InfinitePlaceholderData<Response["data"], Response["error"], Paths, Method, Path>;
|
|
@@ -44,6 +81,10 @@ interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaTy
|
|
|
44
81
|
type MethodResponse<CreatedClient extends OpenapiQueryClient<any, any>, Method extends HttpMethod, Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media> ? PathsWithMethod<Paths, Method> : never, Options = object> = CreatedClient extends OpenapiQueryClient<infer Paths extends {
|
|
45
82
|
[key: string]: any;
|
|
46
83
|
}, infer Media extends MediaType> ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>["data"]> : never;
|
|
84
|
+
/**
|
|
85
|
+
* Hooks and query options for a fetch client. A `QueryClient` other than the one from the React
|
|
86
|
+
* context is passed to each hook as its last argument; `createClient` does not take one.
|
|
87
|
+
*/
|
|
47
88
|
declare function createClient<Paths extends {}, Media extends MediaType = MediaType>(client: Client<Paths, Media>): OpenapiQueryClient<Paths, Media>;
|
|
48
89
|
|
|
49
90
|
declare namespace createClient {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { UseQueryOptions, DataTag, SkipToken, QueryClient, UseQueryResult, UseSuspenseQueryOptions, UseSuspenseQueryResult, UseInfiniteQueryOptions, InfiniteData, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
|
|
2
|
-
import { MaybeOptionalInit, FetchResponse, PathOperation,
|
|
3
|
-
import { HttpMethod, PathsWithMethod, MediaType } from 'openapi-typesafe-helpers';
|
|
2
|
+
import { MaybeOptionalInit, FetchResponse, PathOperation, Client } from 'openapi-typesafe-fetch';
|
|
3
|
+
import { HttpMethod, PathsWithMethod, MediaType, RequiredKeysOf } from 'openapi-typesafe-helpers';
|
|
4
4
|
|
|
5
5
|
type InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;
|
|
6
|
+
/**
|
|
7
|
+
* Query data of a response type: a success without a body is `undefined` in the fetch client and
|
|
8
|
+
* `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).
|
|
9
|
+
*/
|
|
6
10
|
type QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;
|
|
7
11
|
type InitWithUnknowns<Init> = Init & {
|
|
8
12
|
[key: string]: unknown;
|
|
9
13
|
};
|
|
10
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The arguments after the path: the init, required when the operation requires a parameter or a
|
|
16
|
+
* body, then `Rest`. The condition is openapi-react-query's, as `InitParam` of the fetch client
|
|
17
|
+
* is openapi-fetch's (ADR-030, decision 2): TypeScript relates two conditional types only when
|
|
18
|
+
* their `extends` types are the same, so a hook type copied from openapi-react-query takes these
|
|
19
|
+
* hooks. For a union of inits, `MaybeOptionalInit` leaves out `undefined` when one is required,
|
|
20
|
+
* so `Parameters<typeof $api.queryOptions>` still requires it then (upstream #2178).
|
|
21
|
+
*/
|
|
22
|
+
type InitArguments<Init, Rest extends unknown[]> = RequiredKeysOf<Init> extends never ? [InitWithUnknowns<Init>?, ...Rest] : [InitWithUnknowns<Init>, ...Rest];
|
|
11
23
|
type QueryKey<Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init = MaybeOptionalInit<Paths[Path], Method>> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];
|
|
24
|
+
/**
|
|
25
|
+
* The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different
|
|
26
|
+
* data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream
|
|
27
|
+
* #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime
|
|
28
|
+
* named export next to the default export would change what `require()` returns.
|
|
29
|
+
*/
|
|
12
30
|
declare const INFINITE_QUERY_KEY_MARKER = "infinite";
|
|
13
31
|
type InfiniteQueryKey<Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];
|
|
14
32
|
type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryKey" | "queryFn">>(method: Method, path: Path, ...[init, options]: InitArguments<Init, [Options?]>) => NoInfer<Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryFn" | "queryKey"> & {
|
|
@@ -16,17 +34,36 @@ type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <Method e
|
|
|
16
34
|
queryFn: Exclude<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>["queryFn"], SkipToken | undefined>;
|
|
17
35
|
}>;
|
|
18
36
|
type UseQueryMethod<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryKey" | "queryFn">>(method: Method, url: Path, ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>) => UseQueryResult<InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, Response["error"]>;
|
|
37
|
+
/**
|
|
38
|
+
* The type of the page params: the initial one and the ones `getNextPageParam` and
|
|
39
|
+
* `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a
|
|
40
|
+
* return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot
|
|
41
|
+
* infer the return type of a callback that computes it from an unannotated `lastPageParam`,
|
|
42
|
+
* whose type depends on that return type, and gives `never` or `unknown`.
|
|
43
|
+
*/
|
|
19
44
|
type PageParam<Initial, Next, Previous> = IsUntyped<Next> extends true ? unknown : [Previous] extends [NoPreviousPageParam] ? NullishToUnknown<Initial | NonNullable<Next>> : IsUntyped<Previous> extends true ? unknown : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;
|
|
45
|
+
/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */
|
|
20
46
|
declare const NO_PREVIOUS_PAGE_PARAM: unique symbol;
|
|
21
47
|
type NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;
|
|
22
48
|
type IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;
|
|
49
|
+
/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */
|
|
23
50
|
type NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;
|
|
51
|
+
/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */
|
|
24
52
|
type PageParamFunction<Result> = (...args: never) => Result;
|
|
53
|
+
/**
|
|
54
|
+
* Inference sites of the page param types. Intersected before `Options`, so TanStack's own
|
|
55
|
+
* inference in the implementation reads the full signatures of `Options`.
|
|
56
|
+
*/
|
|
25
57
|
interface PageParamSources<Initial, Next, Previous> {
|
|
26
58
|
initialPageParam: Initial;
|
|
27
59
|
getNextPageParam: PageParamFunction<Next>;
|
|
28
60
|
getPreviousPageParam?: PageParamFunction<Previous>;
|
|
29
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types
|
|
64
|
+
* a `(previousData) => previousData` callback while it still infers the page param, so it got
|
|
65
|
+
* `unknown` there and failed against the typed page param.
|
|
66
|
+
*/
|
|
30
67
|
type InfinitePlaceholderData<Data, Error, Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>> = UseInfiniteQueryOptions<Data, Error, InfiniteData<Data, unknown>, InfiniteQueryKey<Paths, Method, Path>, unknown>["placeholderData"];
|
|
31
68
|
type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseInfiniteQueryOptions<Response["data"], Response["error"], InferSelectReturnType<InfiniteData<Response["data"], PageParam<Initial, Next, Previous>>, Options["select"]>, InfiniteQueryKey<Paths, Method, Path>, PageParam<Initial, Next, Previous>>, "queryKey" | "queryFn" | "placeholderData"> & {
|
|
32
69
|
placeholderData?: InfinitePlaceholderData<Response["data"], Response["error"], Paths, Method, Path>;
|
|
@@ -44,6 +81,10 @@ interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaTy
|
|
|
44
81
|
type MethodResponse<CreatedClient extends OpenapiQueryClient<any, any>, Method extends HttpMethod, Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media> ? PathsWithMethod<Paths, Method> : never, Options = object> = CreatedClient extends OpenapiQueryClient<infer Paths extends {
|
|
45
82
|
[key: string]: any;
|
|
46
83
|
}, infer Media extends MediaType> ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>["data"]> : never;
|
|
84
|
+
/**
|
|
85
|
+
* Hooks and query options for a fetch client. A `QueryClient` other than the one from the React
|
|
86
|
+
* context is passed to each hook as its last argument; `createClient` does not take one.
|
|
87
|
+
*/
|
|
47
88
|
declare function createClient<Paths extends {}, Media extends MediaType = MediaType>(client: Client<Paths, Media>): OpenapiQueryClient<Paths, Media>;
|
|
48
89
|
|
|
49
90
|
export { createClient as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { UseQueryOptions, DataTag, SkipToken, QueryClient, UseQueryResult, UseSuspenseQueryOptions, UseSuspenseQueryResult, UseInfiniteQueryOptions, InfiniteData, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
|
|
2
|
-
import { MaybeOptionalInit, FetchResponse, PathOperation,
|
|
3
|
-
import { HttpMethod, PathsWithMethod, MediaType } from 'openapi-typesafe-helpers';
|
|
2
|
+
import { MaybeOptionalInit, FetchResponse, PathOperation, Client } from 'openapi-typesafe-fetch';
|
|
3
|
+
import { HttpMethod, PathsWithMethod, MediaType, RequiredKeysOf } from 'openapi-typesafe-helpers';
|
|
4
4
|
|
|
5
5
|
type InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;
|
|
6
|
+
/**
|
|
7
|
+
* Query data of a response type: a success without a body is `undefined` in the fetch client and
|
|
8
|
+
* `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).
|
|
9
|
+
*/
|
|
6
10
|
type QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;
|
|
7
11
|
type InitWithUnknowns<Init> = Init & {
|
|
8
12
|
[key: string]: unknown;
|
|
9
13
|
};
|
|
10
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The arguments after the path: the init, required when the operation requires a parameter or a
|
|
16
|
+
* body, then `Rest`. The condition is openapi-react-query's, as `InitParam` of the fetch client
|
|
17
|
+
* is openapi-fetch's (ADR-030, decision 2): TypeScript relates two conditional types only when
|
|
18
|
+
* their `extends` types are the same, so a hook type copied from openapi-react-query takes these
|
|
19
|
+
* hooks. For a union of inits, `MaybeOptionalInit` leaves out `undefined` when one is required,
|
|
20
|
+
* so `Parameters<typeof $api.queryOptions>` still requires it then (upstream #2178).
|
|
21
|
+
*/
|
|
22
|
+
type InitArguments<Init, Rest extends unknown[]> = RequiredKeysOf<Init> extends never ? [InitWithUnknowns<Init>?, ...Rest] : [InitWithUnknowns<Init>, ...Rest];
|
|
11
23
|
type QueryKey<Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init = MaybeOptionalInit<Paths[Path], Method>> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];
|
|
24
|
+
/**
|
|
25
|
+
* The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different
|
|
26
|
+
* data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream
|
|
27
|
+
* #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime
|
|
28
|
+
* named export next to the default export would change what `require()` returns.
|
|
29
|
+
*/
|
|
12
30
|
declare const INFINITE_QUERY_KEY_MARKER = "infinite";
|
|
13
31
|
type InfiniteQueryKey<Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];
|
|
14
32
|
type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryKey" | "queryFn">>(method: Method, path: Path, ...[init, options]: InitArguments<Init, [Options?]>) => NoInfer<Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryFn" | "queryKey"> & {
|
|
@@ -16,17 +34,36 @@ type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <Method e
|
|
|
16
34
|
queryFn: Exclude<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>["queryFn"], SkipToken | undefined>;
|
|
17
35
|
}>;
|
|
18
36
|
type UseQueryMethod<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseQueryOptions<QueryData<Response["data"]>, Response["error"], InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, QueryKey<Paths, Method, Path>>, "queryKey" | "queryFn">>(method: Method, url: Path, ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>) => UseQueryResult<InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>, Response["error"]>;
|
|
37
|
+
/**
|
|
38
|
+
* The type of the page params: the initial one and the ones `getNextPageParam` and
|
|
39
|
+
* `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a
|
|
40
|
+
* return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot
|
|
41
|
+
* infer the return type of a callback that computes it from an unannotated `lastPageParam`,
|
|
42
|
+
* whose type depends on that return type, and gives `never` or `unknown`.
|
|
43
|
+
*/
|
|
19
44
|
type PageParam<Initial, Next, Previous> = IsUntyped<Next> extends true ? unknown : [Previous] extends [NoPreviousPageParam] ? NullishToUnknown<Initial | NonNullable<Next>> : IsUntyped<Previous> extends true ? unknown : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;
|
|
45
|
+
/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */
|
|
20
46
|
declare const NO_PREVIOUS_PAGE_PARAM: unique symbol;
|
|
21
47
|
type NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;
|
|
22
48
|
type IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;
|
|
49
|
+
/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */
|
|
23
50
|
type NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;
|
|
51
|
+
/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */
|
|
24
52
|
type PageParamFunction<Result> = (...args: never) => Result;
|
|
53
|
+
/**
|
|
54
|
+
* Inference sites of the page param types. Intersected before `Options`, so TanStack's own
|
|
55
|
+
* inference in the implementation reads the full signatures of `Options`.
|
|
56
|
+
*/
|
|
25
57
|
interface PageParamSources<Initial, Next, Previous> {
|
|
26
58
|
initialPageParam: Initial;
|
|
27
59
|
getNextPageParam: PageParamFunction<Next>;
|
|
28
60
|
getPreviousPageParam?: PageParamFunction<Previous>;
|
|
29
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types
|
|
64
|
+
* a `(previousData) => previousData` callback while it still infers the page param, so it got
|
|
65
|
+
* `unknown` there and failed against the typed page param.
|
|
66
|
+
*/
|
|
30
67
|
type InfinitePlaceholderData<Data, Error, Paths extends {}, Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>> = UseInfiniteQueryOptions<Data, Error, InfiniteData<Data, unknown>, InfiniteQueryKey<Paths, Method, Path>, unknown>["placeholderData"];
|
|
31
68
|
type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>, Init extends MaybeOptionalInit<Paths[Path], Method>, Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, Options extends Omit<UseInfiniteQueryOptions<Response["data"], Response["error"], InferSelectReturnType<InfiniteData<Response["data"], PageParam<Initial, Next, Previous>>, Options["select"]>, InfiniteQueryKey<Paths, Method, Path>, PageParam<Initial, Next, Previous>>, "queryKey" | "queryFn" | "placeholderData"> & {
|
|
32
69
|
placeholderData?: InfinitePlaceholderData<Response["data"], Response["error"], Paths, Method, Path>;
|
|
@@ -44,6 +81,10 @@ interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaTy
|
|
|
44
81
|
type MethodResponse<CreatedClient extends OpenapiQueryClient<any, any>, Method extends HttpMethod, Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media> ? PathsWithMethod<Paths, Method> : never, Options = object> = CreatedClient extends OpenapiQueryClient<infer Paths extends {
|
|
45
82
|
[key: string]: any;
|
|
46
83
|
}, infer Media extends MediaType> ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>["data"]> : never;
|
|
84
|
+
/**
|
|
85
|
+
* Hooks and query options for a fetch client. A `QueryClient` other than the one from the React
|
|
86
|
+
* context is passed to each hook as its last argument; `createClient` does not take one.
|
|
87
|
+
*/
|
|
47
88
|
declare function createClient<Paths extends {}, Media extends MediaType = MediaType>(client: Client<Paths, Media>): OpenapiQueryClient<Paths, Media>;
|
|
48
89
|
|
|
49
90
|
export { createClient as default };
|
package/dist/index.mjs
CHANGED
|
@@ -31,8 +31,15 @@ function createClient(client) {
|
|
|
31
31
|
});
|
|
32
32
|
return {
|
|
33
33
|
queryOptions,
|
|
34
|
-
useQuery: (method, path, ...[init, options, queryClient]) => useQuery(
|
|
35
|
-
|
|
34
|
+
useQuery: (method, path, ...[init, options, queryClient]) => useQuery(
|
|
35
|
+
queryOptions(method, path, init, options),
|
|
36
|
+
queryClient
|
|
37
|
+
),
|
|
38
|
+
useSuspenseQuery: (method, path, ...[init, options, queryClient]) => useSuspenseQuery(
|
|
39
|
+
queryOptions(method, path, init, options),
|
|
40
|
+
queryClient
|
|
41
|
+
),
|
|
42
|
+
// biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)
|
|
36
43
|
useInfiniteQuery: (method, path, init, options, queryClient) => {
|
|
37
44
|
const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;
|
|
38
45
|
const { queryKey } = queryOptions(method, path, init);
|
|
@@ -43,13 +50,18 @@ function createClient(client) {
|
|
|
43
50
|
queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER],
|
|
44
51
|
// The request comes from the arguments; the key only identifies the cache entry.
|
|
45
52
|
queryFn: async ({ pageParam, signal }) => {
|
|
46
|
-
const result = await fn(
|
|
53
|
+
const result = await fn(
|
|
54
|
+
path,
|
|
55
|
+
// biome-ignore lint/suspicious/noExplicitAny: a generic init; see queryFn
|
|
56
|
+
pageInit(init, { pageParamName, pageParam, signal })
|
|
57
|
+
);
|
|
47
58
|
throwIfFailed(result);
|
|
48
59
|
return result.data;
|
|
49
60
|
},
|
|
50
61
|
...restOptions,
|
|
51
62
|
// Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack
|
|
52
63
|
// passes the previous data of this query, with its own page params, and takes it back.
|
|
64
|
+
// biome-ignore lint/suspicious/noExplicitAny: TanStack's type carries its own page params
|
|
53
65
|
placeholderData
|
|
54
66
|
},
|
|
55
67
|
queryClient
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import {\n type DataTag,\n type InfiniteData,\n type QueryClient,\n type QueryFunctionContext,\n type SkipToken,\n type UseInfiniteQueryOptions,\n type UseInfiniteQueryResult,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n type UseSuspenseQueryOptions,\n type UseSuspenseQueryResult,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseQuery,\n} from \"@tanstack/react-query\";\nimport type {\n ClientMethod,\n DefaultParamsOption,\n Client as FetchClient,\n FetchResponse,\n IsInitOptional,\n MaybeOptionalInit,\n PathOperation,\n} from \"openapi-typesafe-fetch\";\nimport type { HttpMethod, MediaType, PathsWithMethod } from \"openapi-typesafe-helpers\";\n\n// Helper type to dynamically infer the type from the `select` property\ntype InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;\n\n/**\n * Query data of a response type: a success without a body is `undefined` in the fetch client and\n * `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).\n */\ntype QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;\n\ntype InitWithUnknowns<Init> = Init & { [key: string]: unknown };\n\n/**\n * The arguments after the path: the init, required when the operation requires a parameter or a\n * body, then `Rest`. `IsInitOptional` keeps the init optional in\n * `Parameters<typeof $api.queryOptions>`, where it is the union of all inits (upstream #2178).\n */\ntype InitArguments<Init, Rest extends unknown[]> =\n IsInitOptional<Init> extends true ? [InitWithUnknowns<Init>?, ...Rest] : [InitWithUnknowns<Init>, ...Rest];\n\nexport type QueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init = MaybeOptionalInit<Paths[Path], Method>,\n> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];\n\n/**\n * The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different\n * data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream\n * #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime\n * named export next to the default export would change what `require()` returns.\n */\nconst INFINITE_QUERY_KEY_MARKER = \"infinite\";\n\nexport type InfiniteQueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];\n\nexport type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n path: Path,\n ...[init, options]: InitArguments<Init, [Options?]>\n) => NoInfer<\n Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryFn\" | \"queryKey\"\n > & {\n queryKey: DataTag<QueryKey<Paths, Method, Path>, QueryData<Response[\"data\"]>, Response[\"error\"]>;\n queryFn: Exclude<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >[\"queryFn\"],\n SkipToken | undefined\n >;\n }\n>;\n\nexport type UseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseQueryResult<InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>, Response[\"error\"]>;\n\n/**\n * The type of the page params: the initial one and the ones `getNextPageParam` and\n * `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a\n * return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot\n * infer the return type of a callback that computes it from an unannotated `lastPageParam`,\n * whose type depends on that return type, and gives `never` or `unknown`.\n */\ntype PageParam<Initial, Next, Previous> =\n IsUntyped<Next> extends true\n ? unknown\n : [Previous] extends [NoPreviousPageParam]\n ? NullishToUnknown<Initial | NonNullable<Next>>\n : IsUntyped<Previous> extends true\n ? unknown\n : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;\n\n/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */\ndeclare const NO_PREVIOUS_PAGE_PARAM: unique symbol;\ntype NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;\n\ntype IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;\n\n/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */\ntype NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;\n\n/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */\ntype PageParamFunction<Result> = (...args: never) => Result;\n\n/**\n * Inference sites of the page param types. Intersected before `Options`, so TanStack's own\n * inference in the implementation reads the full signatures of `Options`.\n */\ninterface PageParamSources<Initial, Next, Previous> {\n initialPageParam: Initial;\n getNextPageParam: PageParamFunction<Next>;\n getPreviousPageParam?: PageParamFunction<Previous>;\n}\n\n/**\n * `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types\n * a `(previousData) => previousData` callback while it still infers the page param, so it got\n * `unknown` there and failed against the typed page param.\n */\ntype InfinitePlaceholderData<\n Data,\n Error,\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = UseInfiniteQueryOptions<\n Data,\n Error,\n InfiniteData<Data, unknown>,\n InfiniteQueryKey<Paths, Method, Path>,\n unknown\n>[\"placeholderData\"];\n\nexport type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseInfiniteQueryOptions<\n Response[\"data\"],\n Response[\"error\"],\n InferSelectReturnType<InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>, Options[\"select\"]>,\n InfiniteQueryKey<Paths, Method, Path>,\n PageParam<Initial, Next, Previous>\n >,\n \"queryKey\" | \"queryFn\" | \"placeholderData\"\n > & {\n placeholderData?: InfinitePlaceholderData<Response[\"data\"], Response[\"error\"], Paths, Method, Path>;\n pageParamName?: string;\n },\n Initial = unknown,\n Next = never,\n Previous = NoPreviousPageParam,\n>(\n method: Method,\n url: Path,\n init: InitWithUnknowns<Init>,\n options: PageParamSources<Initial, Next, Previous> & Options,\n queryClient?: QueryClient,\n) => UseInfiniteQueryResult<\n InferSelectReturnType<InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>, Options[\"select\"]>,\n Response[\"error\"]\n>;\n\nexport type UseSuspenseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n Options extends Omit<\n UseSuspenseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseSuspenseQueryResult<InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>, Response[\"error\"]>;\n\nexport type UseMutationMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>, // note: Required is used to avoid repeating NonNullable in UseQuery types\n TOnMutateResult = unknown,\n>(\n method: Method,\n url: Path,\n options?: Omit<\n UseMutationOptions<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>,\n \"mutationKey\" | \"mutationFn\"\n >,\n queryClient?: QueryClient,\n) => UseMutationResult<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>;\n\nexport interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaType> {\n queryOptions: QueryOptionsFunction<Paths, Media>;\n useQuery: UseQueryMethod<Paths, Media>;\n useSuspenseQuery: UseSuspenseQueryMethod<Paths, Media>;\n useInfiniteQuery: UseInfiniteQueryMethod<Paths, Media>;\n useMutation: UseMutationMethod<Paths, Media>;\n}\n\nexport type MethodResponse<\n CreatedClient extends OpenapiQueryClient<any, any>,\n Method extends HttpMethod,\n Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media>\n ? PathsWithMethod<Paths, Method>\n : never,\n Options = object,\n> =\n CreatedClient extends OpenapiQueryClient<infer Paths extends { [key: string]: any }, infer Media extends MediaType>\n ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>[\"data\"]>\n : never;\n\n/**\n * Throw the error of a failed fetch result. `if (error)` missed two failures: a non-OK response with\n * an empty body (`error` is `undefined`, upstream #2070, #2829) and a falsy error body such as `0`.\n */\nfunction throwIfFailed(result: { error?: unknown; response: Response }): void {\n if (result.error !== undefined || !result.response.ok) {\n throw result.error;\n }\n}\n\n/** The query parameter that carries the page param unless `pageParamName` names another. */\nconst DEFAULT_PAGE_PARAM_NAME = \"cursor\";\n\ninterface PageRequest {\n readonly pageParamName: string;\n readonly pageParam: unknown;\n readonly signal: AbortSignal;\n}\n\n/**\n * The request init of one page. An `undefined` page param (the first page with\n * `initialPageParam: undefined`) is left out instead of being sent as `0` (upstream #2715, #2340,\n * ADR-022), so a cursor API gets no cursor on the first page.\n */\nfunction pageInit(init: unknown, { pageParamName, pageParam, signal }: PageRequest) {\n const { params, ...rest } = (init ?? {}) as { params?: { query?: DefaultParamsOption } };\n const page = pageParam === undefined ? {} : { [pageParamName]: pageParam };\n return { ...rest, signal, params: { ...params, query: { ...params?.query, ...page } } };\n}\n\n// TODO: Add the ability to bring queryClient as argument\nexport default function createClient<Paths extends {}, Media extends MediaType = MediaType>(\n client: FetchClient<Paths, Media>,\n): OpenapiQueryClient<Paths, Media> {\n const queryFn = async <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>>({\n queryKey,\n signal,\n }: QueryFunctionContext<QueryKey<Paths, Method, Path>>) => {\n // While `Path` is generic, TypeScript cannot work out that the key is a tuple: the init is a\n // conditional type per operation (`MaybeOptionalInit`), and the key's type waits for it.\n const [method, path, init] = queryKey as readonly [Method, Path, unknown?];\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n const result = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any\n throwIfFailed(result);\n // TanStack Query rejects `undefined` query data. The fetch client gives `undefined` for every\n // success without a body: a 204, `Content-Length: 0`, an empty chunked body, a HEAD request\n // (upstream #2234). A parsed body is never `undefined`, so this changes nothing else.\n return result.data ?? null;\n };\n\n const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({\n queryKey: (init === undefined ? ([method, path] as const) : ([method, path, init] as const)) as DataTag<\n QueryKey<Paths, typeof method, typeof path>,\n any,\n any\n >,\n queryFn,\n ...options,\n });\n\n return {\n queryOptions,\n useQuery: (method, path, ...[init, options, queryClient]) =>\n useQuery(queryOptions(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),\n useSuspenseQuery: (method, path, ...[init, options, queryClient]) =>\n useSuspenseQuery(queryOptions(method, path, init as InitWithUnknowns<typeof init>, options), queryClient),\n useInfiniteQuery: (method, path, init, options, queryClient) => {\n const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;\n const { queryKey } = queryOptions(method, path, init);\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n return useInfiniteQuery(\n {\n queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER] as const,\n // The request comes from the arguments; the key only identifies the cache entry.\n queryFn: async ({ pageParam, signal }) => {\n const result = await fn(path, pageInit(init, { pageParamName, pageParam, signal }) as any);\n throwIfFailed(result);\n // An empty page stays `undefined`, as in upstream: TanStack Query rejects only\n // `undefined` query data, and the infinite query data is the `{ pages }` object.\n return result.data;\n },\n ...restOptions,\n // Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack\n // passes the previous data of this query, with its own page params, and takes it back.\n placeholderData: placeholderData as any,\n },\n queryClient,\n );\n },\n useMutation: (method, path, options, queryClient) =>\n useMutation(\n {\n mutationKey: [method, path],\n mutationFn: async (init) => {\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n const result = await fn(path, init as InitWithUnknowns<typeof init>);\n throwIfFailed(result);\n // TanStack Query accepts `undefined` mutation data, so a success without a body stays\n // `undefined`, as in upstream; only query data needs `null` (upstream #2234). The cast\n // only bridges the generic types: `Response[\"data\"]` still has `undefined` for an\n // operation with a response without a body.\n const { data } = result;\n return data as Exclude<typeof data, undefined>;\n },\n ...options,\n },\n queryClient,\n ),\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import {\n type DataTag,\n type InfiniteData,\n type QueryClient,\n type QueryFunctionContext,\n type SkipToken,\n type UseInfiniteQueryOptions,\n type UseInfiniteQueryResult,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n type UseSuspenseQueryOptions,\n type UseSuspenseQueryResult,\n useInfiniteQuery,\n useMutation,\n useQuery,\n useSuspenseQuery,\n} from \"@tanstack/react-query\";\nimport type {\n ClientMethod,\n DefaultParamsOption,\n Client as FetchClient,\n FetchResponse,\n MaybeOptionalInit,\n PathOperation,\n} from \"openapi-typesafe-fetch\";\nimport type {\n HttpMethod,\n MediaType,\n PathsWithMethod,\n RequiredKeysOf,\n} from \"openapi-typesafe-helpers\";\n\n// Helper type to dynamically infer the type from the `select` property\ntype InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;\n\n/**\n * Query data of a response type: a success without a body is `undefined` in the fetch client and\n * `null` in the query, as `queryFn` resolves it (upstream #2234, #2464).\n */\ntype QueryData<Data> = undefined extends Data ? Exclude<Data, undefined> | null : Data;\n\ntype InitWithUnknowns<Init> = Init & { [key: string]: unknown };\n\n/**\n * The arguments after the path: the init, required when the operation requires a parameter or a\n * body, then `Rest`. The condition is openapi-react-query's, as `InitParam` of the fetch client\n * is openapi-fetch's (ADR-030, decision 2): TypeScript relates two conditional types only when\n * their `extends` types are the same, so a hook type copied from openapi-react-query takes these\n * hooks. For a union of inits, `MaybeOptionalInit` leaves out `undefined` when one is required,\n * so `Parameters<typeof $api.queryOptions>` still requires it then (upstream #2178).\n */\ntype InitArguments<Init, Rest extends unknown[]> =\n RequiredKeysOf<Init> extends never\n ? [InitWithUnknowns<Init>?, ...Rest]\n : [InitWithUnknowns<Init>, ...Rest];\n\nexport type QueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init = MaybeOptionalInit<Paths[Path], Method>,\n> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];\n\n/**\n * The last element of an infinite query's key. `useQuery` and `useInfiniteQuery` cache different\n * data (`TData` and `InfiniteData<TData>`), so the same request must not share one key (upstream\n * #2184, ADR-022). A prefix such as `[method, path]` still matches both. Not exported: a runtime\n * named export next to the default export would change what `require()` returns.\n */\nconst INFINITE_QUERY_KEY_MARKER = \"infinite\";\n\nexport type InfiniteQueryKey<\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = readonly [...QueryKey<Paths, Method, Path>, typeof INFINITE_QUERY_KEY_MARKER];\n\nexport type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n path: Path,\n ...[init, options]: InitArguments<Init, [Options?]>\n) => NoInfer<\n Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryFn\" | \"queryKey\"\n > & {\n queryKey: DataTag<\n QueryKey<Paths, Method, Path>,\n QueryData<Response[\"data\"]>,\n Response[\"error\"]\n >;\n queryFn: Exclude<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >[\"queryFn\"],\n SkipToken | undefined\n >;\n }\n>;\n\nexport type UseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseQueryResult<\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n Response[\"error\"]\n>;\n\n/**\n * The type of the page params: the initial one and the ones `getNextPageParam` and\n * `getPreviousPageParam` return (upstream #2458). It was `unknown`. It stays `unknown` when a\n * return type is untyped, so code that compiled still compiles (roadmap §3): TypeScript cannot\n * infer the return type of a callback that computes it from an unannotated `lastPageParam`,\n * whose type depends on that return type, and gives `never` or `unknown`.\n */\ntype PageParam<Initial, Next, Previous> =\n IsUntyped<Next> extends true\n ? unknown\n : [Previous] extends [NoPreviousPageParam]\n ? NullishToUnknown<Initial | NonNullable<Next>>\n : IsUntyped<Previous> extends true\n ? unknown\n : NullishToUnknown<Initial | NonNullable<Next> | NonNullable<Previous>>;\n\n/** The `Previous` type parameter when the options have no `getPreviousPageParam`. */\ndeclare const NO_PREVIOUS_PAGE_PARAM: unique symbol;\ntype NoPreviousPageParam = typeof NO_PREVIOUS_PAGE_PARAM;\n\ntype IsUntyped<T> = [T] extends [never] ? true : unknown extends T ? true : false;\n\n/** Only `undefined` or `null` names no type: a cursor API's `initialPageParam: undefined`. */\ntype NullishToUnknown<T> = [T] extends [undefined | null] ? unknown : T;\n\n/** Read only for the return type; `never` arguments add no inference for TanStack's page param. */\ntype PageParamFunction<Result> = (...args: never) => Result;\n\n/**\n * Inference sites of the page param types. Intersected before `Options`, so TanStack's own\n * inference in the implementation reads the full signatures of `Options`.\n */\ninterface PageParamSources<Initial, Next, Previous> {\n initialPageParam: Initial;\n getNextPageParam: PageParamFunction<Next>;\n getPreviousPageParam?: PageParamFunction<Previous>;\n}\n\n/**\n * `placeholderData` of `useInfiniteQuery`, with an `unknown` page param as before: TypeScript types\n * a `(previousData) => previousData` callback while it still infers the page param, so it got\n * `unknown` there and failed against the typed page param.\n */\ntype InfinitePlaceholderData<\n Data,\n Error,\n Paths extends {},\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n> = UseInfiniteQueryOptions<\n Data,\n Error,\n InfiniteData<Data, unknown>,\n InfiniteQueryKey<Paths, Method, Path>,\n unknown\n>[\"placeholderData\"];\n\nexport type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseInfiniteQueryOptions<\n Response[\"data\"],\n Response[\"error\"],\n InferSelectReturnType<\n InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>,\n Options[\"select\"]\n >,\n InfiniteQueryKey<Paths, Method, Path>,\n PageParam<Initial, Next, Previous>\n >,\n \"queryKey\" | \"queryFn\" | \"placeholderData\"\n > & {\n placeholderData?: InfinitePlaceholderData<\n Response[\"data\"],\n Response[\"error\"],\n Paths,\n Method,\n Path\n >;\n pageParamName?: string;\n },\n Initial = unknown,\n Next = never,\n Previous = NoPreviousPageParam,\n // biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)\n>(\n method: Method,\n url: Path,\n init: InitWithUnknowns<Init>,\n options: PageParamSources<Initial, Next, Previous> & Options,\n queryClient?: QueryClient,\n) => UseInfiniteQueryResult<\n InferSelectReturnType<\n InfiniteData<Response[\"data\"], PageParam<Initial, Next, Previous>>,\n Options[\"select\"]\n >,\n Response[\"error\"]\n>;\n\nexport type UseSuspenseQueryMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n Options extends Omit<\n UseSuspenseQueryOptions<\n QueryData<Response[\"data\"]>,\n Response[\"error\"],\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n QueryKey<Paths, Method, Path>\n >,\n \"queryKey\" | \"queryFn\"\n >,\n>(\n method: Method,\n url: Path,\n ...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>\n) => UseSuspenseQueryResult<\n InferSelectReturnType<QueryData<Response[\"data\"]>, Options[\"select\"]>,\n Response[\"error\"]\n>;\n\nexport type UseMutationMethod<Paths extends {}, Media extends MediaType> = <\n Method extends HttpMethod,\n Path extends PathsWithMethod<Paths, Method>,\n Init extends MaybeOptionalInit<Paths[Path], Method>,\n // note: Required is used to avoid repeating NonNullable in UseQuery types\n Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,\n TOnMutateResult = unknown,\n>(\n method: Method,\n url: Path,\n options?: Omit<\n UseMutationOptions<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>,\n \"mutationKey\" | \"mutationFn\"\n >,\n queryClient?: QueryClient,\n) => UseMutationResult<Response[\"data\"], Response[\"error\"], Init, TOnMutateResult>;\n\nexport interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaType> {\n queryOptions: QueryOptionsFunction<Paths, Media>;\n useQuery: UseQueryMethod<Paths, Media>;\n useSuspenseQuery: UseSuspenseQueryMethod<Paths, Media>;\n useInfiniteQuery: UseInfiniteQueryMethod<Paths, Media>;\n useMutation: UseMutationMethod<Paths, Media>;\n}\n\nexport type MethodResponse<\n // biome-ignore lint/suspicious/noExplicitAny: matches a client of any paths, as upstream's\n CreatedClient extends OpenapiQueryClient<any, any>,\n Method extends HttpMethod,\n Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media>\n ? PathsWithMethod<Paths, Method>\n : never,\n Options = object,\n> =\n CreatedClient extends OpenapiQueryClient<\n // biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces\n infer Paths extends { [key: string]: any },\n infer Media extends MediaType\n >\n ? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>[\"data\"]>\n : never;\n\n/**\n * Throw the error of a failed fetch result. `if (error)` missed two failures: a non-OK response\n * with an empty body (`error` is `undefined`, upstream #2070, #2829) and a falsy error body such as\n * `0`.\n */\nfunction throwIfFailed(result: { error?: unknown; response: Response }): void {\n if (result.error !== undefined || !result.response.ok) {\n throw result.error;\n }\n}\n\n/** The query parameter that carries the page param unless `pageParamName` names another. */\nconst DEFAULT_PAGE_PARAM_NAME = \"cursor\";\n\ninterface PageRequest {\n readonly pageParamName: string;\n readonly pageParam: unknown;\n readonly signal: AbortSignal;\n}\n\n/**\n * The request init of one page. An `undefined` page param (the first page with\n * `initialPageParam: undefined`) is left out instead of being sent as `0` (upstream #2715, #2340,\n * ADR-022), so a cursor API gets no cursor on the first page.\n */\nfunction pageInit(init: unknown, { pageParamName, pageParam, signal }: PageRequest) {\n const { params, ...rest } = (init ?? {}) as { params?: { query?: DefaultParamsOption } };\n const page = pageParam === undefined ? {} : { [pageParamName]: pageParam };\n return { ...rest, signal, params: { ...params, query: { ...params?.query, ...page } } };\n}\n\n/**\n * Hooks and query options for a fetch client. A `QueryClient` other than the one from the React\n * context is passed to each hook as its last argument; `createClient` does not take one.\n */\n// biome-ignore lint/complexity/noExcessiveLinesPerFunction: inherited, kept until 1.1 (ADR-036)\nexport default function createClient<Paths extends {}, Media extends MediaType = MediaType>(\n client: FetchClient<Paths, Media>,\n): OpenapiQueryClient<Paths, Media> {\n const queryFn = async <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>>({\n queryKey,\n signal,\n }: QueryFunctionContext<QueryKey<Paths, Method, Path>>) => {\n // While `Path` is generic, TypeScript cannot work out that the key is a tuple: the init is a\n // conditional type per operation (`MaybeOptionalInit`), and the key's type waits for it.\n const [method, path, init] = queryKey as readonly [Method, Path, unknown?];\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n // The init is `unknown` here. An `any` init lets the call infer the generic response type\n // that `queryOptions` declares; a typed init makes TypeScript compare two unresolved\n // conditional types, which it cannot do.\n // biome-ignore lint/suspicious/noExplicitAny: see the comment above\n const result = await fn(path, { signal, ...(init as any) });\n throwIfFailed(result);\n // TanStack Query rejects `undefined` query data. The fetch client gives `undefined` for every\n // success without a body: a 204, `Content-Length: 0`, an empty chunked body, a HEAD request\n // (upstream #2234). A parsed body is never `undefined`, so this changes nothing else.\n return result.data ?? null;\n };\n\n const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({\n queryKey: (init === undefined\n ? ([method, path] as const)\n : ([method, path, init] as const)) as DataTag<\n QueryKey<Paths, typeof method, typeof path>,\n // biome-ignore lint/suspicious/noExplicitAny: the declared return type sets the data type\n any,\n // biome-ignore lint/suspicious/noExplicitAny: the declared return type sets the error type\n any\n >,\n queryFn,\n ...options,\n });\n\n return {\n queryOptions,\n useQuery: (method, path, ...[init, options, queryClient]) =>\n useQuery(\n queryOptions(method, path, init as InitWithUnknowns<typeof init>, options),\n queryClient,\n ),\n useSuspenseQuery: (method, path, ...[init, options, queryClient]) =>\n useSuspenseQuery(\n queryOptions(method, path, init as InitWithUnknowns<typeof init>, options),\n queryClient,\n ),\n // biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)\n useInfiniteQuery: (method, path, init, options, queryClient) => {\n const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;\n const { queryKey } = queryOptions(method, path, init);\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n return useInfiniteQuery(\n {\n queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER] as const,\n // The request comes from the arguments; the key only identifies the cache entry.\n queryFn: async ({ pageParam, signal }) => {\n const result = await fn(\n path,\n // biome-ignore lint/suspicious/noExplicitAny: a generic init; see queryFn\n pageInit(init, { pageParamName, pageParam, signal }) as any,\n );\n throwIfFailed(result);\n // An empty page stays `undefined`, as in upstream: TanStack Query rejects only\n // `undefined` query data, and the infinite query data is the `{ pages }` object.\n return result.data;\n },\n ...restOptions,\n // Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack\n // passes the previous data of this query, with its own page params, and takes it back.\n // biome-ignore lint/suspicious/noExplicitAny: TanStack's type carries its own page params\n placeholderData: placeholderData as any,\n },\n queryClient,\n );\n },\n useMutation: (method, path, options, queryClient) =>\n useMutation(\n {\n mutationKey: [method, path],\n mutationFn: async (init) => {\n const mth = method.toUpperCase() as Uppercase<typeof method>;\n const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;\n const result = await fn(path, init as InitWithUnknowns<typeof init>);\n throwIfFailed(result);\n // TanStack Query accepts `undefined` mutation data, so a success without a body stays\n // `undefined`, as in upstream; only query data needs `null` (upstream #2234). The cast\n // only bridges the generic types: `Response[\"data\"]` still has `undefined` for an\n // operation with a response without a body.\n const { data } = result;\n return data as Exclude<typeof data, undefined>;\n },\n ...options,\n },\n queryClient,\n ),\n };\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openapi-typesafe-react-query",
|
|
3
3
|
"description": "Fast, type-safe @tanstack/react-query client to work with your OpenAPI schema.",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.3",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Fedor Chishchin",
|
|
7
7
|
"url": "https://github.com/fedorello"
|
|
@@ -50,26 +50,26 @@
|
|
|
50
50
|
"tanstack"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"openapi-typesafe-helpers": "^1.0.0-rc.
|
|
53
|
+
"openapi-typesafe-helpers": "^1.0.0-rc.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@tanstack/react-query": "5.
|
|
56
|
+
"@tanstack/react-query": "5.106.1",
|
|
57
57
|
"@testing-library/react": "16.3.3",
|
|
58
58
|
"@types/react": "19.3.0",
|
|
59
59
|
"@types/react-dom": "19.3.0",
|
|
60
|
-
"@vitejs/plugin-react": "6.1
|
|
61
|
-
"
|
|
62
|
-
"jsdom": "30.1.3",
|
|
60
|
+
"@vitejs/plugin-react": "6.2.1",
|
|
61
|
+
"jsdom": "31.0.0",
|
|
63
62
|
"msw": "3.0.3",
|
|
64
|
-
"openapi-typesafe": "^1.0.0-rc.
|
|
65
|
-
"openapi-typesafe-fetch": "^1.0.0-rc.
|
|
63
|
+
"openapi-typesafe": "^1.0.0-rc.3",
|
|
64
|
+
"openapi-typesafe-fetch": "^1.0.0-rc.3",
|
|
66
65
|
"react": "19.3.1",
|
|
67
66
|
"react-dom": "19.3.1",
|
|
68
|
-
"react-error-boundary": "6.1.6"
|
|
67
|
+
"react-error-boundary": "6.1.6",
|
|
68
|
+
"vite-node": "6.0.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@tanstack/react-query": "^5.80.0",
|
|
72
|
-
"openapi-typesafe-fetch": "^1.0.0-rc.
|
|
72
|
+
"openapi-typesafe-fetch": "^1.0.0-rc.3"
|
|
73
73
|
},
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=22"
|
|
@@ -78,9 +78,10 @@
|
|
|
78
78
|
"build": "unbuild",
|
|
79
79
|
"dev": "tsc -p tsconfig.build.json --watch",
|
|
80
80
|
"format": "biome format . --write",
|
|
81
|
-
"lint": "pnpm run lint:js && pnpm run lint:ts",
|
|
81
|
+
"lint": "pnpm run lint:js && pnpm run lint:ts && pnpm run lint:sizes",
|
|
82
82
|
"lint:js": "biome check .",
|
|
83
83
|
"lint:ts": "tsc --noEmit",
|
|
84
|
+
"lint:sizes": "vite-node ../openapi-typesafe/scripts/size-rules/check-sizes.ts src",
|
|
84
85
|
"generate-types": "openapi-typesafe test/fixtures/api.yaml -o test/fixtures/api.d.ts && openapi-typesafe test/fixtures/query.yaml -o test/fixtures/query.d.ts",
|
|
85
86
|
"test": "pnpm run generate-types && pnpm run test:js && pnpm run test:exports",
|
|
86
87
|
"test:js": "vitest run",
|
package/src/index.ts
CHANGED
|
@@ -22,11 +22,15 @@ import type {
|
|
|
22
22
|
DefaultParamsOption,
|
|
23
23
|
Client as FetchClient,
|
|
24
24
|
FetchResponse,
|
|
25
|
-
IsInitOptional,
|
|
26
25
|
MaybeOptionalInit,
|
|
27
26
|
PathOperation,
|
|
28
27
|
} from "openapi-typesafe-fetch";
|
|
29
|
-
import type {
|
|
28
|
+
import type {
|
|
29
|
+
HttpMethod,
|
|
30
|
+
MediaType,
|
|
31
|
+
PathsWithMethod,
|
|
32
|
+
RequiredKeysOf,
|
|
33
|
+
} from "openapi-typesafe-helpers";
|
|
30
34
|
|
|
31
35
|
// Helper type to dynamically infer the type from the `select` property
|
|
32
36
|
type InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => infer R ? R : TData;
|
|
@@ -41,11 +45,16 @@ type InitWithUnknowns<Init> = Init & { [key: string]: unknown };
|
|
|
41
45
|
|
|
42
46
|
/**
|
|
43
47
|
* The arguments after the path: the init, required when the operation requires a parameter or a
|
|
44
|
-
* body, then `Rest`. `
|
|
45
|
-
*
|
|
48
|
+
* body, then `Rest`. The condition is openapi-react-query's, as `InitParam` of the fetch client
|
|
49
|
+
* is openapi-fetch's (ADR-030, decision 2): TypeScript relates two conditional types only when
|
|
50
|
+
* their `extends` types are the same, so a hook type copied from openapi-react-query takes these
|
|
51
|
+
* hooks. For a union of inits, `MaybeOptionalInit` leaves out `undefined` when one is required,
|
|
52
|
+
* so `Parameters<typeof $api.queryOptions>` still requires it then (upstream #2178).
|
|
46
53
|
*/
|
|
47
54
|
type InitArguments<Init, Rest extends unknown[]> =
|
|
48
|
-
|
|
55
|
+
RequiredKeysOf<Init> extends never
|
|
56
|
+
? [InitWithUnknowns<Init>?, ...Rest]
|
|
57
|
+
: [InitWithUnknowns<Init>, ...Rest];
|
|
49
58
|
|
|
50
59
|
export type QueryKey<
|
|
51
60
|
Paths extends {},
|
|
@@ -72,7 +81,8 @@ export type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <
|
|
|
72
81
|
Method extends HttpMethod,
|
|
73
82
|
Path extends PathsWithMethod<Paths, Method>,
|
|
74
83
|
Init extends MaybeOptionalInit<Paths[Path], Method>,
|
|
75
|
-
|
|
84
|
+
// note: Required is used to avoid repeating NonNullable in UseQuery types
|
|
85
|
+
Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,
|
|
76
86
|
Options extends Omit<
|
|
77
87
|
UseQueryOptions<
|
|
78
88
|
QueryData<Response["data"]>,
|
|
@@ -96,7 +106,11 @@ export type QueryOptionsFunction<Paths extends {}, Media extends MediaType> = <
|
|
|
96
106
|
>,
|
|
97
107
|
"queryFn" | "queryKey"
|
|
98
108
|
> & {
|
|
99
|
-
queryKey: DataTag<
|
|
109
|
+
queryKey: DataTag<
|
|
110
|
+
QueryKey<Paths, Method, Path>,
|
|
111
|
+
QueryData<Response["data"]>,
|
|
112
|
+
Response["error"]
|
|
113
|
+
>;
|
|
100
114
|
queryFn: Exclude<
|
|
101
115
|
UseQueryOptions<
|
|
102
116
|
QueryData<Response["data"]>,
|
|
@@ -113,7 +127,8 @@ export type UseQueryMethod<Paths extends {}, Media extends MediaType> = <
|
|
|
113
127
|
Method extends HttpMethod,
|
|
114
128
|
Path extends PathsWithMethod<Paths, Method>,
|
|
115
129
|
Init extends MaybeOptionalInit<Paths[Path], Method>,
|
|
116
|
-
|
|
130
|
+
// note: Required is used to avoid repeating NonNullable in UseQuery types
|
|
131
|
+
Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,
|
|
117
132
|
Options extends Omit<
|
|
118
133
|
UseQueryOptions<
|
|
119
134
|
QueryData<Response["data"]>,
|
|
@@ -127,7 +142,10 @@ export type UseQueryMethod<Paths extends {}, Media extends MediaType> = <
|
|
|
127
142
|
method: Method,
|
|
128
143
|
url: Path,
|
|
129
144
|
...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>
|
|
130
|
-
) => UseQueryResult<
|
|
145
|
+
) => UseQueryResult<
|
|
146
|
+
InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>,
|
|
147
|
+
Response["error"]
|
|
148
|
+
>;
|
|
131
149
|
|
|
132
150
|
/**
|
|
133
151
|
* The type of the page params: the initial one and the ones `getNextPageParam` and
|
|
@@ -195,18 +213,28 @@ export type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> =
|
|
|
195
213
|
UseInfiniteQueryOptions<
|
|
196
214
|
Response["data"],
|
|
197
215
|
Response["error"],
|
|
198
|
-
InferSelectReturnType<
|
|
216
|
+
InferSelectReturnType<
|
|
217
|
+
InfiniteData<Response["data"], PageParam<Initial, Next, Previous>>,
|
|
218
|
+
Options["select"]
|
|
219
|
+
>,
|
|
199
220
|
InfiniteQueryKey<Paths, Method, Path>,
|
|
200
221
|
PageParam<Initial, Next, Previous>
|
|
201
222
|
>,
|
|
202
223
|
"queryKey" | "queryFn" | "placeholderData"
|
|
203
224
|
> & {
|
|
204
|
-
placeholderData?: InfinitePlaceholderData<
|
|
225
|
+
placeholderData?: InfinitePlaceholderData<
|
|
226
|
+
Response["data"],
|
|
227
|
+
Response["error"],
|
|
228
|
+
Paths,
|
|
229
|
+
Method,
|
|
230
|
+
Path
|
|
231
|
+
>;
|
|
205
232
|
pageParamName?: string;
|
|
206
233
|
},
|
|
207
234
|
Initial = unknown,
|
|
208
235
|
Next = never,
|
|
209
236
|
Previous = NoPreviousPageParam,
|
|
237
|
+
// biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)
|
|
210
238
|
>(
|
|
211
239
|
method: Method,
|
|
212
240
|
url: Path,
|
|
@@ -214,7 +242,10 @@ export type UseInfiniteQueryMethod<Paths extends {}, Media extends MediaType> =
|
|
|
214
242
|
options: PageParamSources<Initial, Next, Previous> & Options,
|
|
215
243
|
queryClient?: QueryClient,
|
|
216
244
|
) => UseInfiniteQueryResult<
|
|
217
|
-
InferSelectReturnType<
|
|
245
|
+
InferSelectReturnType<
|
|
246
|
+
InfiniteData<Response["data"], PageParam<Initial, Next, Previous>>,
|
|
247
|
+
Options["select"]
|
|
248
|
+
>,
|
|
218
249
|
Response["error"]
|
|
219
250
|
>;
|
|
220
251
|
|
|
@@ -222,7 +253,8 @@ export type UseSuspenseQueryMethod<Paths extends {}, Media extends MediaType> =
|
|
|
222
253
|
Method extends HttpMethod,
|
|
223
254
|
Path extends PathsWithMethod<Paths, Method>,
|
|
224
255
|
Init extends MaybeOptionalInit<Paths[Path], Method>,
|
|
225
|
-
|
|
256
|
+
// note: Required is used to avoid repeating NonNullable in UseQuery types
|
|
257
|
+
Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,
|
|
226
258
|
Options extends Omit<
|
|
227
259
|
UseSuspenseQueryOptions<
|
|
228
260
|
QueryData<Response["data"]>,
|
|
@@ -236,13 +268,17 @@ export type UseSuspenseQueryMethod<Paths extends {}, Media extends MediaType> =
|
|
|
236
268
|
method: Method,
|
|
237
269
|
url: Path,
|
|
238
270
|
...[init, options, queryClient]: InitArguments<Init, [Options?, QueryClient?]>
|
|
239
|
-
) => UseSuspenseQueryResult<
|
|
271
|
+
) => UseSuspenseQueryResult<
|
|
272
|
+
InferSelectReturnType<QueryData<Response["data"]>, Options["select"]>,
|
|
273
|
+
Response["error"]
|
|
274
|
+
>;
|
|
240
275
|
|
|
241
276
|
export type UseMutationMethod<Paths extends {}, Media extends MediaType> = <
|
|
242
277
|
Method extends HttpMethod,
|
|
243
278
|
Path extends PathsWithMethod<Paths, Method>,
|
|
244
279
|
Init extends MaybeOptionalInit<Paths[Path], Method>,
|
|
245
|
-
|
|
280
|
+
// note: Required is used to avoid repeating NonNullable in UseQuery types
|
|
281
|
+
Response extends Required<FetchResponse<PathOperation<Paths, Path, Method>, Init, Media>>,
|
|
246
282
|
TOnMutateResult = unknown,
|
|
247
283
|
>(
|
|
248
284
|
method: Method,
|
|
@@ -263,6 +299,7 @@ export interface OpenapiQueryClient<Paths extends {}, Media extends MediaType =
|
|
|
263
299
|
}
|
|
264
300
|
|
|
265
301
|
export type MethodResponse<
|
|
302
|
+
// biome-ignore lint/suspicious/noExplicitAny: matches a client of any paths, as upstream's
|
|
266
303
|
CreatedClient extends OpenapiQueryClient<any, any>,
|
|
267
304
|
Method extends HttpMethod,
|
|
268
305
|
Path extends CreatedClient extends OpenapiQueryClient<infer Paths, infer _Media>
|
|
@@ -270,13 +307,18 @@ export type MethodResponse<
|
|
|
270
307
|
: never,
|
|
271
308
|
Options = object,
|
|
272
309
|
> =
|
|
273
|
-
CreatedClient extends OpenapiQueryClient<
|
|
310
|
+
CreatedClient extends OpenapiQueryClient<
|
|
311
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown rejects the generated interfaces
|
|
312
|
+
infer Paths extends { [key: string]: any },
|
|
313
|
+
infer Media extends MediaType
|
|
314
|
+
>
|
|
274
315
|
? NonNullable<FetchResponse<Paths[Path][Method], Options, Media>["data"]>
|
|
275
316
|
: never;
|
|
276
317
|
|
|
277
318
|
/**
|
|
278
|
-
* Throw the error of a failed fetch result. `if (error)` missed two failures: a non-OK response
|
|
279
|
-
* an empty body (`error` is `undefined`, upstream #2070, #2829) and a falsy error body such as
|
|
319
|
+
* Throw the error of a failed fetch result. `if (error)` missed two failures: a non-OK response
|
|
320
|
+
* with an empty body (`error` is `undefined`, upstream #2070, #2829) and a falsy error body such as
|
|
321
|
+
* `0`.
|
|
280
322
|
*/
|
|
281
323
|
function throwIfFailed(result: { error?: unknown; response: Response }): void {
|
|
282
324
|
if (result.error !== undefined || !result.response.ok) {
|
|
@@ -304,7 +346,11 @@ function pageInit(init: unknown, { pageParamName, pageParam, signal }: PageReque
|
|
|
304
346
|
return { ...rest, signal, params: { ...params, query: { ...params?.query, ...page } } };
|
|
305
347
|
}
|
|
306
348
|
|
|
307
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Hooks and query options for a fetch client. A `QueryClient` other than the one from the React
|
|
351
|
+
* context is passed to each hook as its last argument; `createClient` does not take one.
|
|
352
|
+
*/
|
|
353
|
+
// biome-ignore lint/complexity/noExcessiveLinesPerFunction: inherited, kept until 1.1 (ADR-036)
|
|
308
354
|
export default function createClient<Paths extends {}, Media extends MediaType = MediaType>(
|
|
309
355
|
client: FetchClient<Paths, Media>,
|
|
310
356
|
): OpenapiQueryClient<Paths, Media> {
|
|
@@ -317,7 +363,11 @@ export default function createClient<Paths extends {}, Media extends MediaType =
|
|
|
317
363
|
const [method, path, init] = queryKey as readonly [Method, Path, unknown?];
|
|
318
364
|
const mth = method.toUpperCase() as Uppercase<typeof method>;
|
|
319
365
|
const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
|
|
320
|
-
|
|
366
|
+
// The init is `unknown` here. An `any` init lets the call infer the generic response type
|
|
367
|
+
// that `queryOptions` declares; a typed init makes TypeScript compare two unresolved
|
|
368
|
+
// conditional types, which it cannot do.
|
|
369
|
+
// biome-ignore lint/suspicious/noExplicitAny: see the comment above
|
|
370
|
+
const result = await fn(path, { signal, ...(init as any) });
|
|
321
371
|
throwIfFailed(result);
|
|
322
372
|
// TanStack Query rejects `undefined` query data. The fetch client gives `undefined` for every
|
|
323
373
|
// success without a body: a 204, `Content-Length: 0`, an empty chunked body, a HEAD request
|
|
@@ -326,9 +376,13 @@ export default function createClient<Paths extends {}, Media extends MediaType =
|
|
|
326
376
|
};
|
|
327
377
|
|
|
328
378
|
const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({
|
|
329
|
-
queryKey: (init === undefined
|
|
379
|
+
queryKey: (init === undefined
|
|
380
|
+
? ([method, path] as const)
|
|
381
|
+
: ([method, path, init] as const)) as DataTag<
|
|
330
382
|
QueryKey<Paths, typeof method, typeof path>,
|
|
383
|
+
// biome-ignore lint/suspicious/noExplicitAny: the declared return type sets the data type
|
|
331
384
|
any,
|
|
385
|
+
// biome-ignore lint/suspicious/noExplicitAny: the declared return type sets the error type
|
|
332
386
|
any
|
|
333
387
|
>,
|
|
334
388
|
queryFn,
|
|
@@ -338,9 +392,16 @@ export default function createClient<Paths extends {}, Media extends MediaType =
|
|
|
338
392
|
return {
|
|
339
393
|
queryOptions,
|
|
340
394
|
useQuery: (method, path, ...[init, options, queryClient]) =>
|
|
341
|
-
useQuery(
|
|
395
|
+
useQuery(
|
|
396
|
+
queryOptions(method, path, init as InitWithUnknowns<typeof init>, options),
|
|
397
|
+
queryClient,
|
|
398
|
+
),
|
|
342
399
|
useSuspenseQuery: (method, path, ...[init, options, queryClient]) =>
|
|
343
|
-
useSuspenseQuery(
|
|
400
|
+
useSuspenseQuery(
|
|
401
|
+
queryOptions(method, path, init as InitWithUnknowns<typeof init>, options),
|
|
402
|
+
queryClient,
|
|
403
|
+
),
|
|
404
|
+
// biome-ignore lint/complexity/useMaxParams: public signature of 1.x (ADR-036)
|
|
344
405
|
useInfiniteQuery: (method, path, init, options, queryClient) => {
|
|
345
406
|
const { pageParamName = DEFAULT_PAGE_PARAM_NAME, placeholderData, ...restOptions } = options;
|
|
346
407
|
const { queryKey } = queryOptions(method, path, init);
|
|
@@ -351,7 +412,11 @@ export default function createClient<Paths extends {}, Media extends MediaType =
|
|
|
351
412
|
queryKey: [...queryKey, INFINITE_QUERY_KEY_MARKER] as const,
|
|
352
413
|
// The request comes from the arguments; the key only identifies the cache entry.
|
|
353
414
|
queryFn: async ({ pageParam, signal }) => {
|
|
354
|
-
const result = await fn(
|
|
415
|
+
const result = await fn(
|
|
416
|
+
path,
|
|
417
|
+
// biome-ignore lint/suspicious/noExplicitAny: a generic init; see queryFn
|
|
418
|
+
pageInit(init, { pageParamName, pageParam, signal }) as any,
|
|
419
|
+
);
|
|
355
420
|
throwIfFailed(result);
|
|
356
421
|
// An empty page stays `undefined`, as in upstream: TanStack Query rejects only
|
|
357
422
|
// `undefined` query data, and the infinite query data is the `{ pages }` object.
|
|
@@ -360,6 +425,7 @@ export default function createClient<Paths extends {}, Media extends MediaType =
|
|
|
360
425
|
...restOptions,
|
|
361
426
|
// Typed with an `unknown` page param for the caller (`InfinitePlaceholderData`). TanStack
|
|
362
427
|
// passes the previous data of this query, with its own page params, and takes it back.
|
|
428
|
+
// biome-ignore lint/suspicious/noExplicitAny: TanStack's type carries its own page params
|
|
363
429
|
placeholderData: placeholderData as any,
|
|
364
430
|
},
|
|
365
431
|
queryClient,
|