New:Socket for Asana Is Now Available.Learn more
Get Started

@7nohe/openapi-react-query-codegen

Package Overview
Dependencies
Maintainers
1
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@7nohe/openapi-react-query-codegen - npm Package Compare versions

Comparing version
3.0.1
to
3.0.2
+16
dist/tsmorph/operationNames.d.mts
import type { GenerationContext, OperationInfo } from "../types.mjs";
/**
* Resolve the generated Data type name for an operation, falling back to
* unknown when the SDK signature exposes no Data type.
* See OperationInfo.dataTypeName for why the name is read from the signature
* instead of derived from the method name (#213).
*/
export declare function getDataTypeName(op: OperationInfo): string;
/**
* Get the error type string based on client type.
* The Error type shares its stem with the Data type — hey-api mints both from
* the operationId (see OperationInfo.dataTypeName, #213) — so the stem comes
* from `dataTypeName` rather than the method name. The modelNames probe stays
* because operations without error responses have no generated Error type.
*/
export declare function getErrorType(op: OperationInfo, ctx: GenerationContext): string;
/**
* Resolve the generated Data type name for an operation, falling back to
* unknown when the SDK signature exposes no Data type.
* See OperationInfo.dataTypeName for why the name is read from the signature
* instead of derived from the method name (#213).
*/
export function getDataTypeName(op) {
return op.dataTypeName ?? "unknown";
}
/**
* Get the error type string based on client type.
* The Error type shares its stem with the Data type — hey-api mints both from
* the operationId (see OperationInfo.dataTypeName, #213) — so the stem comes
* from `dataTypeName` rather than the method name. The modelNames probe stays
* because operations without error responses have no generated Error type.
*/
export function getErrorType(op, ctx) {
const stem = op.dataTypeName
? op.dataTypeName.replace(/Data$/, "")
: op.capitalizedMethodName;
const errorTypeName = `${stem}Error`;
const errorType = ctx.modelNames.includes(errorTypeName)
? errorTypeName
: "unknown";
if (ctx.client === "@hey-api/client-axios") {
return `AxiosError<${errorType}>`;
}
return errorType;
}
+1
-1

@@ -1,2 +0,2 @@

import type { Project } from "ts-morph";
import { type Project } from "ts-morph";
import type { GenerationContext, OperationInfo } from "./types.mjs";

@@ -3,0 +3,0 @@ /**

@@ -0,1 +1,2 @@

import { Node, } from "ts-morph";
import ts from "typescript";

@@ -39,4 +40,21 @@ import { capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromVariable, getShortType, getVariableArrowFunctionParameters, } from "./common.mjs";

/**
* Get paginatable methods by checking if their Data type has the pageParam in query property.
* Read the operation's Data type name from the first type argument of the
* SDK options parameter (`Options<XData, ThrowOnError>`).
* See OperationInfo.dataTypeName for why the name is read from the signature
* instead of derived from the method name (#213).
*/
function getDataTypeNameFromSignature(optionsParam) {
const typeNode = optionsParam?.getTypeNode();
if (!typeNode || !Node.isTypeReference(typeNode))
return undefined;
if (typeNode.getTypeName().getText() !== "Options")
return undefined;
const [dataTypeArg] = typeNode.getTypeArguments();
return dataTypeArg?.getText();
}
/**
* Get paginatable Data types by checking if they have the pageParam in their query property.
* Uses TypeScript compiler API for accurate AST traversal.
* The map is keyed by the Data type name (e.g., "FindPetsData") so callers can
* look it up with the name read from the SDK signature.
*/

@@ -74,9 +92,5 @@ function getPaginatableMethods(project, pageParam) {

if (pageParamNode) {
// Extract method name from Data type name (e.g., "FindPetsData" -> "findPets")
const methodName = key.slice(0, -4); // Remove "Data" suffix
// Convert first letter to lowercase
const methodNameLower = methodName.charAt(0).toLowerCase() + methodName.slice(1);
const pageParamType = pageParamNode.type?.getText(modelsFile.compilerNode);
const resolvedType = typeChecker.getTypeAtLocation(pageParamNode.type ?? pageParamNode);
paginatableMethods.set(methodNameLower, {
paginatableMethods.set(key, {
type: pageParamType ?? "unknown",

@@ -105,3 +119,6 @@ typeKind: getPageParamTypeKind(resolvedType),

const allParamsOptional = sdkParams.length === 0 || sdkParams[0].isOptional();
const pageParamInfo = paginatableMethods.get(methodName);
const dataTypeName = getDataTypeNameFromSignature(sdkParams[0]);
const pageParamInfo = dataTypeName
? paginatableMethods.get(dataTypeName)
: undefined;
const isPaginatable = httpMethod === "GET" && pageParamInfo !== undefined;

@@ -111,2 +128,3 @@ return {

capitalizedMethodName: capitalizeFirstLetter(methodName),
dataTypeName,
httpMethod,

@@ -113,0 +131,0 @@ jsDoc: desc.jsDoc,

@@ -33,3 +33,3 @@ import { type TypeAliasDeclarationStructure, type VariableStatementStructure } from "ts-morph";

*/
export declare function buildQueryKeyFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
export declare function buildQueryKeyFn(op: OperationInfo): VariableStatementStructure;
/**

@@ -36,0 +36,0 @@ * Build mutation key function.

import { StructureKind, VariableDeclarationKind, } from "ts-morph";
import { getDataTypeName } from "./operationNames.mjs";
/**

@@ -81,6 +82,4 @@ * Build the default response type alias.

*/
export function buildQueryKeyFn(op, ctx) {
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
? `${op.capitalizedMethodName}Data`
: "unknown";
export function buildQueryKeyFn(op) {
const dataTypeName = getDataTypeName(op);
const params = [];

@@ -130,8 +129,4 @@ const defaultValue = op.allParamsOptional ? " = {}" : "";

export function buildInfiniteClientOptionsType(op, ctx) {
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
? `${op.capitalizedMethodName}Data`
: "unknown";
const type = dataTypeName === "unknown"
? "Options<unknown, true>"
: `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`;
const dataTypeName = getDataTypeName(op);
const type = `Omit<Options<${dataTypeName}, true>, "query"> & { query?: Omit<NonNullable<${dataTypeName}["query"]>, "${ctx.pageParam}"> }`;
return {

@@ -138,0 +133,0 @@ kind: StructureKind.TypeAlias,

import { StructureKind, VariableDeclarationKind, } from "ts-morph";
import { SDK_CALL_ARGS } from "./buildQueryHooks.mjs";
import { getDataTypeName, getErrorType } from "./operationNames.mjs";
/**
* Get the error type string based on client type.
*/
function getErrorType(op, ctx) {
const errorTypeName = `${op.capitalizedMethodName}Error`;
// Operations without error responses have no generated Error type
const errorType = ctx.modelNames.includes(errorTypeName)
? errorTypeName
: "unknown";
if (ctx.client === "@hey-api/client-axios") {
return `AxiosError<${errorType}>`;
}
return errorType;
}
/**
* Build useMutation hook.

@@ -33,6 +20,3 @@ * Example:

const dataTypeDefault = `Common.${op.capitalizedMethodName}MutationResult`;
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
? `${op.capitalizedMethodName}Data`
: "unknown";
const optionsType = `Options<${dataTypeName}, true>`;
const optionsType = `Options<${getDataTypeName(op)}, true>`;
const mutationFn = `clientOptions => ${op.methodName}(${SDK_CALL_ARGS}) as unknown as Promise<TData>`;

@@ -39,0 +23,0 @@ const body = `useMutation<TData, TError, ${optionsType}, TContext>({ mutationKey: Common.Use${op.capitalizedMethodName}KeyFn(mutationKey), mutationFn: ${mutationFn}, ...options })`;

import { type VariableStatementStructure } from "ts-morph";
import type { GenerationContext, OperationInfo } from "../types.mjs";
/**
* Resolve the generated Data type name for an operation, falling back to
* unknown when the operation has no generated Data type.
*/
export declare function getDataTypeName(op: OperationInfo, ctx: GenerationContext): string;
/**
* SDK call arguments shared by every generated queryFn/mutationFn.

@@ -22,3 +17,3 @@ * throwOnError: true forces the SDK call to reject on error responses; the

*/
export declare function buildClientOptionsParam(op: OperationInfo, ctx: GenerationContext): string;
export declare function buildClientOptionsParam(op: OperationInfo): string;
/**

@@ -31,5 +26,16 @@ * Build the clientOptions parameter typed with the page-less infinite

/**
* The type of a single page. TanStack Query instantiates the infinite options
* with this as TQueryFnData, so it is what `getNextPageParam` receives as
* `lastPage` (#203). NonNullable because `throwOnError: true` means a resolved
* page is always present.
*/
export declare function getPageType(op: OperationInfo): string;
/**
* Build the paginated SDK call shared by every infinite query builder.
* The resolved response is narrowed to the page type so it matches the
* TQueryFnData the infinite options are instantiated with (#203). Spelled as
* a cast rather than `!` because generated code is linted downstream, and a
* non-null assertion trips biome's noNonNullAssertion.
*/
export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext, castTData: boolean): string;
export declare function buildPagedQueryFn(op: OperationInfo, ctx: GenerationContext): string;
/**

@@ -56,4 +62,11 @@ * Format the initialPageParam literal. Emits `undefined` when the caller opted

* callers may replace them for custom pagination schemes (#156, #146).
*
* The first type argument is TQueryFnData — a single page — not TData (#203).
* Passing TData there made `getNextPageParam` receive the aggregated
* `InfiniteData<...>` as `lastPage`, so any custom pagination logic failed to
* compile. Only the first three type arguments are written: TanStack Query
* dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so
* positions beyond TData are not stable across the supported peer range.
*/
export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string): string;
export declare function buildOverridableInfiniteOptionsType(optionsTypeName: string, pageType: string): string;
/**

@@ -95,10 +108,10 @@ * Build useQuery hook.

*/
export declare function buildPrefetchFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
export declare function buildPrefetchFn(op: OperationInfo): VariableStatementStructure;
/**
* Build prefetchInfiniteQuery function for a paginatable operation.
* Example:
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">) =>
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, NonNullable<Common.FindPaginatedPetsDefaultResponse>> }) =>
* queryClient.prefetchInfiniteQuery({
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
* initialPageParam: 1,

@@ -120,2 +133,2 @@ * getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,

*/
export declare function buildEnsureQueryDataFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
export declare function buildEnsureQueryDataFn(op: OperationInfo): VariableStatementStructure;
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
import { getDataTypeName, getErrorType } from "./operationNames.mjs";
/**
* Get the error type string based on client type.
*/
function getErrorType(op, ctx) {
const errorTypeName = `${op.capitalizedMethodName}Error`;
// Operations without error responses have no generated Error type
const errorType = ctx.modelNames.includes(errorTypeName)
? errorTypeName
: "unknown";
if (ctx.client === "@hey-api/client-axios") {
return `AxiosError<${errorType}>`;
}
return errorType;
}
/**
* Resolve the generated Data type name for an operation, falling back to
* unknown when the operation has no generated Data type.
*/
export function getDataTypeName(op, ctx) {
return ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
? `${op.capitalizedMethodName}Data`
: "unknown";
}
/**
* SDK call arguments shared by every generated queryFn/mutationFn.

@@ -41,4 +19,4 @@ * throwOnError: true forces the SDK call to reject on error responses; the

*/
export function buildClientOptionsParam(op, ctx) {
const dataTypeName = getDataTypeName(op, ctx);
export function buildClientOptionsParam(op) {
const dataTypeName = getDataTypeName(op);
const hasParams = op.parameters.length > 0;

@@ -61,9 +39,20 @@ if (!hasParams) {

/**
* The type of a single page. TanStack Query instantiates the infinite options
* with this as TQueryFnData, so it is what `getNextPageParam` receives as
* `lastPage` (#203). NonNullable because `throwOnError: true` means a resolved
* page is always present.
*/
export function getPageType(op) {
return `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`;
}
/**
* Build the paginated SDK call shared by every infinite query builder.
* The resolved response is narrowed to the page type so it matches the
* TQueryFnData the infinite options are instantiated with (#203). Spelled as
* a cast rather than `!` because generated code is linted downstream, and a
* non-null assertion trips biome's noNonNullAssertion.
*/
export function buildPagedQueryFn(op, ctx, castTData) {
const dataTypeName = getDataTypeName(op, ctx);
const thenClause = castTData
? ".then(response => response.data as TData) as TData"
: ".then(response => response.data)";
export function buildPagedQueryFn(op, ctx) {
const dataTypeName = getDataTypeName(op);
const thenClause = `.then(response => response.data as ${getPageType(op)})`;
const pageParamType = getPageParamType(op);

@@ -121,5 +110,12 @@ // When the initial page param is omitted, the first request must send no

* callers may replace them for custom pagination schemes (#156, #146).
*
* The first type argument is TQueryFnData — a single page — not TData (#203).
* Passing TData there made `getNextPageParam` receive the aggregated
* `InfiniteData<...>` as `lastPage`, so any custom pagination logic failed to
* compile. Only the first three type arguments are written: TanStack Query
* dropped TQueryData from `UseInfiniteQueryOptions` within the v5 line, so
* positions beyond TData are not stable across the supported peer range.
*/
export function buildOverridableInfiniteOptionsType(optionsTypeName) {
const instantiated = `${optionsTypeName}<TData, TError>`;
export function buildOverridableInfiniteOptionsType(optionsTypeName, pageType) {
const instantiated = `${optionsTypeName}<${pageType}, TError, TData>`;
return `Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<${instantiated}, "initialPageParam" | "getNextPageParam">>`;

@@ -144,3 +140,3 @@ }

const dataTypeDefault = `Common.${op.capitalizedMethodName}DefaultResponse`;
const clientOptionsParam = buildClientOptionsParam(op, ctx);
const clientOptionsParam = buildClientOptionsParam(op);
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;

@@ -169,3 +165,3 @@ const body = `useQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;

const dataTypeDefault = `NonNullable<Common.${op.capitalizedMethodName}DefaultResponse>`;
const clientOptionsParam = buildClientOptionsParam(op, ctx);
const clientOptionsParam = buildClientOptionsParam(op);
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data as TData) as TData`;

@@ -209,3 +205,3 @@ const body = `useSuspenseQuery<TData, TError>({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ...options })`;

: `InfiniteData<${baseDataType}>`;
const queryFn = buildPagedQueryFn(op, ctx, true);
const queryFn = buildPagedQueryFn(op, ctx);
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;

@@ -222,3 +218,3 @@ const body = `${hookCall}({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;

name: hookName,
initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName)}) => ${body}`,
initializer: `<TData = ${dataTypeDefault}, TError = ${errorType}, TQueryKey extends Array<unknown> = unknown[]>(${buildInfiniteClientOptionsParam(op)}, queryKey?: TQueryKey, options?: ${buildOverridableInfiniteOptionsType(optionsTypeName, getPageType(op))}) => ${body}`,
},

@@ -250,3 +246,3 @@ ],

*/
export function buildPrefetchFn(op, ctx) {
export function buildPrefetchFn(op) {
const fnName = `prefetchUse${op.capitalizedMethodName}`;

@@ -265,3 +261,3 @@ const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;

name: fnName,
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op, ctx)}, ${optionsParam}) => ${body}`,
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op)}, ${optionsParam}) => ${body}`,
},

@@ -274,6 +270,6 @@ ],

* Example:
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<Common.FindPaginatedPetsDefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">) =>
* export const prefetchUseFindPaginatedPetsInfinite = (queryClient: QueryClient, clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, options?: Omit<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & Partial<Pick<FetchInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, NonNullable<Common.FindPaginatedPetsDefaultResponse>> }) =>
* queryClient.prefetchInfiniteQuery({
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
* initialPageParam: 1,

@@ -289,5 +285,13 @@ * getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,

const fnName = `prefetchUse${op.capitalizedMethodName}Infinite`;
const queryFn = buildPagedQueryFn(op, ctx, false);
const queryFn = buildPagedQueryFn(op, ctx);
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
const optionsParam = `options?: Omit<FetchInfiniteQueryOptions<Common.${op.capitalizedMethodName}DefaultResponse>, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam">`;
// The pagination fields are re-added as optional overrides: `...options` is
// spread after the defaults, so replacing them works at runtime and must not
// be forbidden by the type (#203). getNextPageParam is spelled out rather
// than Picked, because FetchInfiniteQueryOptions only exposes it on the
// union member that also requires `pages`.
const pageType = getPageType(op);
const instantiated = `FetchInfiniteQueryOptions<${pageType}>`;
const overrides = `Partial<Pick<${instantiated}, "initialPageParam">> & { getNextPageParam?: GetNextPageParamFunction<unknown, ${pageType}> }`;
const optionsParam = `options?: Omit<${instantiated}, "queryKey" | "queryFn" | "initialPageParam" | "getNextPageParam"> & ${overrides}`;
const body = `queryClient.prefetchInfiniteQuery({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;

@@ -318,3 +322,3 @@ return {

*/
export function buildEnsureQueryDataFn(op, ctx) {
export function buildEnsureQueryDataFn(op) {
const fnName = `ensureUse${op.capitalizedMethodName}Data`;

@@ -333,3 +337,3 @@ const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;

name: fnName,
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op, ctx)}, ${optionsParam}) => ${body}`,
initializer: `(queryClient: QueryClient, ${buildClientOptionsParam(op)}, ${optionsParam}) => ${body}`,
},

@@ -336,0 +340,0 @@ ],

@@ -15,3 +15,3 @@ import { type VariableStatementStructure } from "ts-morph";

*/
export declare function buildQueryOptionsFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
export declare function buildQueryOptionsFn(op: OperationInfo): VariableStatementStructure;
/**

@@ -21,10 +21,11 @@ * Build an infiniteQueryOptions factory for a paginatable GET operation.

* Example:
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>, options?: Partial<Pick<UseInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam" | "getNextPageParam">>) =>
* infiniteQueryOptions({
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
* initialPageParam: 1,
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
* ...options,
* });
*/
export declare function buildInfiniteQueryOptionsFn(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure | null;
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, QUERY_SDK_CALL_ARGS, } from "./buildQueryHooks.mjs";
import { buildClientOptionsParam, buildGetNextPageParamExpr, buildInfiniteClientOptionsParam, buildPagedQueryFn, formatInitialPageParam, getPageType, QUERY_SDK_CALL_ARGS, } from "./buildQueryHooks.mjs";
/**

@@ -15,5 +15,5 @@ * Build a queryOptions factory for a GET operation.

*/
export function buildQueryOptionsFn(op, ctx) {
export function buildQueryOptionsFn(op) {
const fnName = `${op.methodName}Options`;
const clientOptionsParam = buildClientOptionsParam(op, ctx);
const clientOptionsParam = buildClientOptionsParam(op);
const queryFn = `({ signal }) => ${op.methodName}(${QUERY_SDK_CALL_ARGS}).then(response => response.data)`;

@@ -39,8 +39,9 @@ const body = `queryOptions({ queryKey: Common.Use${op.capitalizedMethodName}KeyFn(clientOptions, queryKey), queryFn: ${queryFn} })`;

* Example:
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>) =>
* export const findPaginatedPetsInfiniteOptions = (clientOptions: Common.FindPaginatedPetsInfiniteClientOptions = {}, queryKey?: Array<unknown>, options?: Partial<Pick<UseInfiniteQueryOptions<NonNullable<Common.FindPaginatedPetsDefaultResponse>>, "initialPageParam" | "getNextPageParam">>) =>
* infiniteQueryOptions({
* queryKey: Common.UseFindPaginatedPetsInfiniteKeyFn(clientOptions, queryKey),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data),
* queryFn: ({ pageParam }) => findPaginatedPets({ ...clientOptions, query: { ...clientOptions.query, page: pageParam as number }, throwOnError: true } as Options<FindPaginatedPetsData, true>).then(response => response.data as NonNullable<Common.FindPaginatedPetsDefaultResponse>),
* initialPageParam: 1,
* getNextPageParam: (response: unknown) => (response as { nextPage: number }).nextPage,
* ...options,
* });

@@ -53,5 +54,9 @@ */

const fnName = `${op.methodName}InfiniteOptions`;
const queryFn = buildPagedQueryFn(op, ctx, false);
const queryFn = buildPagedQueryFn(op, ctx);
const infiniteOptions = `initialPageParam: ${formatInitialPageParam(ctx, op)}, getNextPageParam: ${buildGetNextPageParamExpr(ctx, op)}`;
const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions} })`;
// Only the pagination fields are overridable here: the factory's return type
// is what every downstream consumer infers from, and a wider options type
// (select, placeholderData, ...) would make that inference ambiguous (#203).
const optionsParam = `options?: Partial<Pick<UseInfiniteQueryOptions<${getPageType(op)}>, "initialPageParam" | "getNextPageParam">>`;
const body = `infiniteQueryOptions({ queryKey: Common.Use${op.capitalizedMethodName}InfiniteKeyFn(clientOptions, queryKey), queryFn: ${queryFn}, ${infiniteOptions}, ...options })`;
return {

@@ -66,3 +71,3 @@ kind: StructureKind.VariableStatement,

name: fnName,
initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown>) => ${body}`,
initializer: `(${buildInfiniteClientOptionsParam(op)}, queryKey?: Array<unknown>, ${optionsParam}) => ${body}`,
},

@@ -69,0 +74,0 @@ ],

@@ -6,28 +6,18 @@ import { OpenApiRqFiles } from "../constants.mjs";

import { buildInfiniteQueryOptionsFn, buildQueryOptionsFn, } from "./buildQueryOptions.mjs";
import { buildAxiosErrorImport, buildClientImport, buildCommonImport, buildModelImport, buildQueryImport, buildQueryOptionsImport, buildServiceImport, createGenerationProject, } from "./projectFactory.mjs";
import { buildCommonFileImports, buildHookFileImports, buildQueryOptionsFileImports, createGenerationProject, } from "./projectFactory.mjs";
/**
* Build imports for common.ts file.
* Add one variable statement per operation, skipping the ones the builder
* declines. A builder returns null when the operation is out of its scope —
* every infinite-query builder returns null for non-paginatable operations,
* which is how the paginatable subset gets selected.
*/
function buildCommonFileImports(ctx) {
const imports = [
buildClientImport(ctx),
buildQueryImport(),
buildServiceImport(ctx),
];
const modelImport = buildModelImport(ctx);
if (modelImport) {
imports.push(modelImport);
function addStatements(sourceFile, operations, build) {
for (const op of operations) {
const statement = build(op);
if (statement) {
sourceFile.addVariableStatement(statement);
}
}
if (ctx.client === "@hey-api/client-axios") {
imports.push(buildAxiosErrorImport());
}
return imports;
}
/**
* Build imports for hook files (queries, suspense, infinite, prefetch, ensure).
*/
function buildHookFileImports(ctx) {
return [buildCommonImport(), ...buildCommonFileImports(ctx)];
}
/**
* Generate the index.ts file content.

@@ -55,3 +45,3 @@ * The content is constant, so no ts-morph project is needed.

sourceFile.addVariableStatement(buildQueryKeyConst(op));
sourceFile.addVariableStatement(buildQueryKeyFn(op, ctx));
sourceFile.addVariableStatement(buildQueryKeyFn(op));
}

@@ -100,24 +90,10 @@ // Add dedicated infinite query types and keys for paginatable operations

// Add imports
const imports = [
buildCommonImport(),
buildQueryOptionsImport(),
buildClientImport(ctx),
buildServiceImport(ctx),
];
const modelImport = buildModelImport(ctx);
if (modelImport) {
imports.push(modelImport);
}
sourceFile.addImportDeclarations(imports);
sourceFile.addImportDeclarations(buildQueryOptionsFileImports(ctx));
// Only GET operations have query options
const getOperations = operations.filter((op) => op.httpMethod === "GET");
for (const op of getOperations) {
sourceFile.addVariableStatement(buildQueryOptionsFn(op, ctx));
sourceFile.addVariableStatement(buildQueryOptionsFn(op));
}
for (const op of getOperations) {
const infiniteOptions = buildInfiniteQueryOptionsFn(op, ctx);
if (infiniteOptions) {
sourceFile.addVariableStatement(infiniteOptions);
}
}
// Add infiniteQueryOptions factories
addStatements(sourceFile, getOperations, (op) => buildInfiniteQueryOptionsFn(op, ctx));
return sourceFile.getFullText();

@@ -139,9 +115,4 @@ }

}
// Add useSuspenseInfiniteQuery hooks for paginatable operations
for (const op of getOperations.filter((o) => o.isPaginatable)) {
const hook = buildUseSuspenseInfiniteQueryHook(op, ctx);
if (hook) {
sourceFile.addVariableStatement(hook);
}
}
// Add useSuspenseInfiniteQuery hooks
addStatements(sourceFile, getOperations, (op) => buildUseSuspenseInfiniteQueryHook(op, ctx));
return sourceFile.getFullText();

@@ -157,11 +128,6 @@ }

sourceFile.addImportDeclarations(buildHookFileImports(ctx));
// Only paginatable GET operations
const paginatableOperations = operations.filter((op) => op.httpMethod === "GET" && op.isPaginatable);
// Only GET operations can be paginatable
const getOperations = operations.filter((op) => op.httpMethod === "GET");
// Add useInfiniteQuery hooks
for (const op of paginatableOperations) {
const hook = buildUseInfiniteQueryHook(op, ctx);
if (hook) {
sourceFile.addVariableStatement(hook);
}
}
addStatements(sourceFile, getOperations, (op) => buildUseInfiniteQueryHook(op, ctx));
return sourceFile.getFullText();

@@ -181,11 +147,6 @@ }

for (const op of getOperations) {
sourceFile.addVariableStatement(buildPrefetchFn(op, ctx));
sourceFile.addVariableStatement(buildPrefetchFn(op));
}
// Add prefetchInfiniteQuery functions for paginatable operations
for (const op of getOperations.filter((o) => o.isPaginatable)) {
const fn = buildPrefetchInfiniteQueryFn(op, ctx);
if (fn) {
sourceFile.addVariableStatement(fn);
}
}
// Add prefetchInfiniteQuery functions
addStatements(sourceFile, getOperations, (op) => buildPrefetchInfiniteQueryFn(op, ctx));
return sourceFile.getFullText();

@@ -205,3 +166,3 @@ }

for (const op of getOperations) {
sourceFile.addVariableStatement(buildEnsureQueryDataFn(op, ctx));
sourceFile.addVariableStatement(buildEnsureQueryDataFn(op));
}

@@ -208,0 +169,0 @@ return sourceFile.getFullText();

@@ -48,1 +48,7 @@ import { type ImportDeclarationStructure, Project } from "ts-morph";

export declare function buildHookFileImports(ctx: GenerationContext): ImportDeclarationStructure[];
/**
* Build all imports needed for the queryOptions file.
* Narrower than the hook file imports: queryOptions only needs the
* queryOptions/infiniteQueryOptions helpers, not the TanStack hooks.
*/
export declare function buildQueryOptionsFileImports(ctx: GenerationContext): ImportDeclarationStructure[];

@@ -56,2 +56,3 @@ import { IndentationText, NewLineKind, Project, QuoteKind, StructureKind, } from "ts-morph";

{ name: "FetchInfiniteQueryOptions", isTypeOnly: true },
{ name: "GetNextPageParamFunction", isTypeOnly: true },
{ name: "EnsureQueryDataOptions", isTypeOnly: true },

@@ -68,3 +69,7 @@ ],

moduleSpecifier: "@tanstack/react-query",
namedImports: [{ name: "queryOptions" }, { name: "infiniteQueryOptions" }],
namedImports: [
{ name: "queryOptions" },
{ name: "infiniteQueryOptions" },
{ name: "UseInfiniteQueryOptions", isTypeOnly: true },
],
};

@@ -142,1 +147,19 @@ }

}
/**
* Build all imports needed for the queryOptions file.
* Narrower than the hook file imports: queryOptions only needs the
* queryOptions/infiniteQueryOptions helpers, not the TanStack hooks.
*/
export function buildQueryOptionsFileImports(ctx) {
const imports = [
buildCommonImport(),
buildQueryOptionsImport(),
buildClientImport(ctx),
buildServiceImport(ctx),
];
const modelImport = buildModelImport(ctx);
if (modelImport) {
imports.push(modelImport);
}
return imports;
}

@@ -10,2 +10,16 @@ /**

capitalizedMethodName: string;
/**
* Generated Data type name read from the SDK function's own
* `Options<XData, ThrowOnError>` signature (e.g., "FindPetsData").
* Not derivable from the method name: for digit-leading operationIds
* hey-api prefixes the function but strips the digits from the type (#213).
* Undefined when the SDK signature exposes no Data type.
*
* The rule this encodes: names hey-api owns (Data, Error) must be read from
* the SDK signature or share this field's stem; names this codegen mints
* itself (DefaultResponse, MutationResult, key fns) may be derived from
* capitalizedMethodName because they anchor back to the SDK via
* `typeof methodName`.
*/
dataTypeName?: string;
/** HTTP method (e.g., "GET", "POST", "PUT", "PATCH", "DELETE") */

@@ -12,0 +26,0 @@ httpMethod: string;

{
"name": "@7nohe/openapi-react-query-codegen",
"version": "3.0.1",
"version": "3.0.2",
"description": "OpenAPI React Query Codegen",

@@ -5,0 +5,0 @@ "bin": {

import { type VariableStatementStructure } from "ts-morph";
import type { GenerationContext, OperationInfo } from "../types.mjs";
/**
* Build query key constant name (e.g., "findPetsQueryKey").
*/
export declare function getQueryKeyName(op: OperationInfo): string;
/**
* Build mutation key constant name (e.g., "addPetMutationKey").
*/
export declare function getMutationKeyName(op: OperationInfo): string;
/**
* Build query key fn name (e.g., "FindPetsQueryKeyFn").
*/
export declare function getQueryKeyFnName(op: OperationInfo): string;
/**
* Build mutation key fn name (e.g., "AddPetMutationKeyFn").
*/
export declare function getMutationKeyFnName(op: OperationInfo): string;
/**
* Build the query key constant export.
* Example: export const findPetsQueryKey = "FindPets";
*/
export declare function buildQueryKeyExport(op: OperationInfo): VariableStatementStructure;
/**
* Build the mutation key constant export.
* Example: export const addPetMutationKey = "AddPet";
*/
export declare function buildMutationKeyExport(op: OperationInfo): VariableStatementStructure;
/**
* Build the query key function export.
* Example:
* export const FindPetsQueryKeyFn = (clientOptions: Options<FindPetsData, true>, queryKey?: Array<unknown>) =>
* [findPetsQueryKey, ...(queryKey ?? [clientOptions])] as const;
*/
export declare function buildQueryKeyFnExport(op: OperationInfo, ctx: GenerationContext): VariableStatementStructure;
/**
* Build the mutation key function export.
* Example:
* export const AddPetMutationKeyFn = (mutationKey?: Array<unknown>) =>
* [addPetMutationKey, ...(mutationKey ?? [])] as const;
*/
export declare function buildMutationKeyFnExport(op: OperationInfo): VariableStatementStructure;
import { StructureKind, VariableDeclarationKind, } from "ts-morph";
/**
* Build query key constant name (e.g., "findPetsQueryKey").
*/
export function getQueryKeyName(op) {
return `${op.methodName}QueryKey`;
}
/**
* Build mutation key constant name (e.g., "addPetMutationKey").
*/
export function getMutationKeyName(op) {
return `${op.methodName}MutationKey`;
}
/**
* Build query key fn name (e.g., "FindPetsQueryKeyFn").
*/
export function getQueryKeyFnName(op) {
return `${op.capitalizedMethodName}QueryKeyFn`;
}
/**
* Build mutation key fn name (e.g., "AddPetMutationKeyFn").
*/
export function getMutationKeyFnName(op) {
return `${op.capitalizedMethodName}MutationKeyFn`;
}
/**
* Build the query key constant export.
* Example: export const findPetsQueryKey = "FindPets";
*/
export function buildQueryKeyExport(op) {
return {
kind: StructureKind.VariableStatement,
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: getQueryKeyName(op),
initializer: `"${op.capitalizedMethodName}"`,
},
],
};
}
/**
* Build the mutation key constant export.
* Example: export const addPetMutationKey = "AddPet";
*/
export function buildMutationKeyExport(op) {
return {
kind: StructureKind.VariableStatement,
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: getMutationKeyName(op),
initializer: `"${op.capitalizedMethodName}"`,
},
],
};
}
/**
* Build the query key function export.
* Example:
* export const FindPetsQueryKeyFn = (clientOptions: Options<FindPetsData, true>, queryKey?: Array<unknown>) =>
* [findPetsQueryKey, ...(queryKey ?? [clientOptions])] as const;
*/
export function buildQueryKeyFnExport(op, ctx) {
const hasParams = op.parameters.length > 0;
const dataTypeName = ctx.modelNames.includes(`${op.capitalizedMethodName}Data`)
? `${op.capitalizedMethodName}Data`
: "unknown";
const params = [];
if (hasParams) {
const defaultValue = op.allParamsOptional ? " = {}" : "";
params.push(`clientOptions: Options<${dataTypeName}, true>${defaultValue}`);
}
params.push("queryKey?: Array<unknown>");
const fallbackArray = hasParams ? "[clientOptions]" : "[]";
const body = `[${getQueryKeyName(op)}, ...(queryKey ?? ${fallbackArray})] as const`;
return {
kind: StructureKind.VariableStatement,
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: getQueryKeyFnName(op),
initializer: `(${params.join(", ")}) => ${body}`,
},
],
};
}
/**
* Build the mutation key function export.
* Example:
* export const AddPetMutationKeyFn = (mutationKey?: Array<unknown>) =>
* [addPetMutationKey, ...(mutationKey ?? [])] as const;
*/
export function buildMutationKeyFnExport(op) {
const body = `[${getMutationKeyName(op)}, ...(mutationKey ?? [])] as const`;
return {
kind: StructureKind.VariableStatement,
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: getMutationKeyFnName(op),
initializer: `(mutationKey?: Array<unknown>) => ${body}`,
},
],
};
}