diff --git a/.changeset/smooth-ties-brush.md b/.changeset/smooth-ties-brush.md new file mode 100644 index 0000000000..1c17966feb --- /dev/null +++ b/.changeset/smooth-ties-brush.md @@ -0,0 +1,5 @@ +--- +'@hey-api/openapi-ts': patch +--- + +feat(plugin): add `@pinia/colada` plugin diff --git a/packages/openapi-ts-tests/main/test/plugins.test.ts b/packages/openapi-ts-tests/main/test/plugins.test.ts index 3b5631c3bb..929b9cfcf6 100644 --- a/packages/openapi-ts-tests/main/test/plugins.test.ts +++ b/packages/openapi-ts-tests/main/test/plugins.test.ts @@ -363,6 +363,15 @@ for (const version of versions) { description: 'generate Fetch API client with TanStack Vue Query plugin with custom names', }, + // TODO: add Pinia Colada snapshots + // { + // config: createConfig({ + // output: 'fetch', + // plugins: ['@pinia/colada', '@hey-api/client-fetch'], + // }), + // description: + // 'generate Fetch API client with Pinia Colada plugin', + // }, { config: createConfig({ output: 'default', diff --git a/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts b/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts index 53680f5b7c..7d9e6e8bd7 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/typescript/operation.ts @@ -9,7 +9,8 @@ import { schemaToType } from './plugin'; import { typesId } from './ref'; import type { HeyApiTypeScriptPlugin, PluginState } from './types'; -const irParametersToIrSchema = ({ +// TODO: exported just for @pinia/colada, remove export once that plugin does not depend on it +export const irParametersToIrSchema = ({ parameters, }: { parameters: Record; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/config.ts b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts new file mode 100644 index 0000000000..259db6167c --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/config.ts @@ -0,0 +1,26 @@ +import { definePluginConfig } from '../../shared/utils/config'; +import { handler } from './plugin'; +import type { PiniaColadaPlugin } from './types'; + +export const defaultConfig: PiniaColadaPlugin['Config'] = { + config: { + enablePaginationOnKey: undefined, + errorHandling: 'specific', + exportFromIndex: false, + groupByTag: false, + importPath: '@pinia/colada', + includeTypes: true, + prefixUse: true, + suffixQueryMutation: true, + useInfiniteQueries: false, + }, + dependencies: ['@hey-api/typescript'], + handler: handler as PiniaColadaPlugin['Handler'], + name: '@pinia/colada', + output: '@pinia/colada', +}; + +/** + * Type helper for `@pinia/colada` plugin, returns {@link Plugin.Config} object + */ +export const defineConfig = definePluginConfig(defaultConfig); diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/index.ts b/packages/openapi-ts/src/plugins/@pinia/colada/index.ts new file mode 100644 index 0000000000..7ea6aa4734 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config'; +export type { PiniaColadaPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts b/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts new file mode 100644 index 0000000000..b8707ecbbc --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/mutation.ts @@ -0,0 +1,29 @@ +import type { GeneratedFile } from '../../../generate/file'; +import type { IR } from '../../../ir/types'; +import type { PiniaColadaPlugin } from './types'; +import { createComposable } from './utils'; + +/** + * Creates a mutation function for an operation + */ +export const createMutationFunction = ({ + context, + file, + operation, + plugin, +}: { + context: IR.Context; + file: GeneratedFile; + operation: IR.OperationObject; + plugin: PiniaColadaPlugin['Instance']; +}) => { + // Allow hooks to customize or skip mutation generation + if ( + plugin.config.onMutation && + plugin.config.onMutation(operation) === false + ) { + return; + } + + createComposable({ context, file, isQuery: false, operation, plugin }); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts new file mode 100644 index 0000000000..65b3ea0d81 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/plugin.ts @@ -0,0 +1,67 @@ +import { clientId } from '../../@hey-api/client-core/utils'; +import { createMutationFunction } from './mutation'; +import { createQueryFunction } from './query'; +import type { PiniaColadaPlugin } from './types'; +import { isQuery } from './utils'; + +export const handler: PiniaColadaPlugin['Handler'] = ({ plugin }) => { + if (!plugin.config.groupByTag) { + plugin.createFile({ + id: plugin.name, + path: plugin.output, + }); + } + + // Create files based on grouping strategy + const getFile = (tag: string) => { + if (!plugin.config.groupByTag) { + return ( + plugin.context.file({ id: plugin.name }) ?? + plugin.createFile({ + id: plugin.name, + path: plugin.output, + }) + ); + } + + const fileId = `${plugin.name}/${tag}`; + return ( + plugin.context.file({ id: fileId }) ?? + plugin.createFile({ + id: fileId, + path: `${plugin.output}/${tag}`, + }) + ); + }; + + plugin.forEach('operation', ({ operation }) => { + const file = getFile(operation.tags?.[0] || 'default'); + + // Determine if the operation should be a query or mutation + if (isQuery(operation, plugin)) { + createQueryFunction({ context: plugin.context, file, operation, plugin }); + } else { + createMutationFunction({ + context: plugin.context, + file, + operation, + plugin, + }); + } + }); + + // Add client import to all generated files + Object.entries(plugin.context.files).forEach(([fileId, file]) => { + if (fileId.startsWith(plugin.name)) { + // Make sure we have a client import + file.import({ + alias: '_heyApiClient', + module: file.relativePathToFile({ + context: plugin.context, + id: clientId, + }), + name: 'client', + }); + } + }); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/query.ts b/packages/openapi-ts/src/plugins/@pinia/colada/query.ts new file mode 100644 index 0000000000..91e7e3c1f0 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/query.ts @@ -0,0 +1,25 @@ +import type { GeneratedFile } from '../../../generate/file'; +import type { IR } from '../../../ir/types'; +import type { PiniaColadaPlugin } from './types'; +import { createComposable } from './utils'; + +/** + * Creates a query function for an operation + */ +export const createQueryFunction = ({ + context, + file, + operation, + plugin, +}: { + context: IR.Context; + file: GeneratedFile; + operation: IR.OperationObject; + plugin: PiniaColadaPlugin['Instance']; +}) => { + if (plugin.config.onQuery && plugin.config.onQuery(operation) === false) { + return; + } + + createComposable({ context, file, isQuery: true, operation, plugin }); +}; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts new file mode 100644 index 0000000000..0282e16896 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/types.d.ts @@ -0,0 +1,217 @@ +import type { IR } from '../../../ir/types'; +// import type { StringCase, StringName } from '../../../types/case'; +import type { DefinePlugin, Plugin } from '../../types'; + +export type UserConfig = Plugin.Name<'@pinia/colada'> & { + /** + * Default cache time for queries in milliseconds. + * + * @default undefined + */ + defaultCacheTime?: number; + /** + * Default stale time for queries in milliseconds. + * + * @default undefined + */ + defaultStaleTime?: number; + /** + * Enable pagination support on this key when found in the query parameters or body. + * + * @default undefined + */ + enablePaginationOnKey?: string; + /** + * How to handle error responses. + * 'unified' - Unified error type for all errors + * 'specific' - Specific error types per operation + * + * @default 'specific' + */ + errorHandling?: 'unified' | 'specific'; + /** + * Should the exports from the generated files be re-exported in the index barrel file? + * + * @default false + */ + exportFromIndex?: boolean; + /** + * Group operations by tag into separate files. + * + * @default false + */ + groupByTag?: boolean; + /** + * Import path for the plugin. + * + * @default '@pinia/colada' + */ + importPath?: string; + /** + * Include types in the generated files. + * + * @default true + */ + includeTypes?: boolean; + /** + * Custom hook to customize or skip mutation generation. + * Return false to skip generating a mutation for this operation. + * + * @default undefined + */ + onMutation?: (operation: IR.OperationObject) => boolean | undefined; + /** + * Custom hook to customize or skip query generation. + * Return false to skip generating a query for this operation. + * + * @default undefined + */ + onQuery?: (operation: IR.OperationObject) => boolean | undefined; + /** + * Plugin output path. + * + * @default '@pinia/colada' + */ + output: string; + /** + * Whether to prefix generated function names with 'use'. + * + * @default true + */ + prefixUse?: boolean; + /** + * Custom hook that determines if an operation should be a query or not. + * Return true to force query, false to force mutation, undefined to use default logic. + * + * @default undefined + */ + resolveQuery?: (operation: IR.OperationObject) => boolean | undefined; + /** + * Custom hook to resolve query key. + * Default is [operation.tags?.[0] || 'default', operation.id] + * + * @default undefined + */ + resolveQueryKey?: (operation: IR.OperationObject) => Array; + /** + * Whether to suffix generated function names with 'Query' or 'Mutation' to indicate the type + * of Pinia Colada operation that is used under the hood. + * + * @default true + */ + suffixQueryMutation?: boolean; + /** + * Use infinite queries. + * + * @default false + */ + useInfiniteQueries?: boolean; +}; + +export type Config = Plugin.Name<'@pinia/colada'> & { + /** + * Default cache time for queries in milliseconds. + * + * @default undefined + */ + defaultCacheTime: number | undefined; + /** + * Default stale time for queries in milliseconds. + * + * @default undefined + */ + defaultStaleTime: number | undefined; + /** + * Enable pagination support on this key when found in the query parameters or body. + * + * @default undefined + */ + enablePaginationOnKey?: string; + /** + * How to handle error responses. + * 'unified' - Unified error type for all errors + * 'specific' - Specific error types per operation + * + * @default 'specific' + */ + errorHandling?: 'unified' | 'specific'; + /** + * Should the exports from the generated files be re-exported in the index barrel file? + * + * @default false + */ + exportFromIndex: boolean; + /** + * Group operations by tag into separate files. + * + * @default false + */ + groupByTag?: boolean; + /** + * Import path for the plugin. + * + * @default '@pinia/colada' + */ + importPath?: string; + /** + * Include types in the generated files. + * + * @default true + */ + includeTypes?: boolean; + /** + * Custom hook to customize or skip mutation generation. + * Return false to skip generating a mutation for this operation. + * + * @default undefined + */ + onMutation?: (operation: IR.OperationObject) => boolean | undefined; + /** + * Custom hook to customize or skip query generation. + * Return false to skip generating a query for this operation. + * + * @default undefined + */ + onQuery?: (operation: IR.OperationObject) => boolean | undefined; + /** + * Plugin output path. + * + * @default '@pinia/colada' + */ + output: string; + /** + * Whether to prefix generated function names with 'use'. + * + * @default true + */ + prefixUse?: boolean; + /** + * Custom hook that determines if an operation should be a query or not. + * Return true to force query, false to force mutation, undefined to use default logic. + * + * @default undefined + */ + resolveQuery?: (operation: IR.OperationObject) => boolean | undefined; + /** + * Custom hook to resolve query key. + * Default is [operation.tags?.[0] || 'default', operation.id] + * + * @default undefined + */ + resolveQueryKey?: (operation: IR.OperationObject) => Array; + /** + * Whether to suffix generated function names with 'Query' or 'Mutation' to indicate the type + * of Pinia Colada operation that is used under the hood. + * + * @default true + */ + suffixQueryMutation?: boolean; + /** + * Use infinite queries. + * + * @default false + */ + useInfiniteQueries?: boolean; +}; + +export type PiniaColadaPlugin = DefinePlugin; diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts new file mode 100644 index 0000000000..258bfad548 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@pinia/colada/utils.ts @@ -0,0 +1,430 @@ +import type { TypeNode } from 'typescript'; + +import type { GeneratedFile } from '../../../generate/file'; +import type { IR } from '../../../ir/types'; +import type { Property } from '../../../tsc'; +import { tsc } from '../../../tsc'; +import { escapeComment } from '../../../utils/escape'; +import { stringCase } from '../../../utils/stringCase'; +import { irParametersToIrSchema } from '../../@hey-api/typescript/operation'; +import { schemaToType } from '../../@hey-api/typescript/plugin'; +import type { PiniaColadaPlugin } from './types'; + +const importIdentifierData = () => ({ + name: '', +}); +const importIdentifierError = () => ({ + name: '', +}); +const importIdentifierResponse = () => ({ + name: '', +}); + +/** + * Determines if an operation should be a query or mutation + */ +export const isQuery = ( + operation: IR.OperationObject, + plugin: PiniaColadaPlugin['Instance'], +): boolean => { + // 1. Check for hook override + const hookResult = plugin.config.resolveQuery?.(operation); + if (hookResult !== undefined) { + return hookResult; + } + + // 2. Use method as primary signal + if (['get', 'head', 'options'].includes(operation.method)) { + return true; + } + + // 3. Consider body presence as secondary signal + // If method is not GET/HEAD/OPTIONS but also has no body schema, likely a query + return !operation.body?.schema; +}; + +/** + * Generates the cache configuration object for a query + */ +export const generateCacheConfig = ( + operation: IR.OperationObject, + plugin: PiniaColadaPlugin['Instance'], +) => { + const obj: Array<{ + key: string; + value: any; + }> = []; + + // Use default stale time if specified in config + if (plugin.config.defaultStaleTime !== undefined) { + obj.push({ + key: 'staleTime', + value: plugin.config.defaultStaleTime, + }); + } + + // Use default cache time if specified in config + if (plugin.config.defaultCacheTime !== undefined) { + obj.push({ + key: 'gcTime', + value: plugin.config.defaultCacheTime, + }); + } + + // Add pagination config if enabled and operation has pagination parameters + if ( + plugin.config.enablePaginationOnKey && + hasPagination(operation, plugin.config.enablePaginationOnKey) + ) { + obj.push({ + key: 'infinite', + value: true, + }); + } + + return obj; +}; + +/** + * Checks if operation has pagination parameters + */ +export const hasPagination = ( + operation: IR.OperationObject, + paginationParam: string, +): boolean => + // Check if operation has pagination parameter + !!operation.parameters?.query?.[paginationParam] || + !!operation.body?.pagination; + +/** + * Generates the function name for an operation + */ +export const generateFunctionName = ( + operation: IR.OperationObject, + isQueryType: boolean, + prefixUse: boolean = true, + suffixQueryMutation: boolean = true, +): string => { + const operationPascalCase = stringCase({ + case: 'PascalCase', + value: operation.id, + }); + const prefix = prefixUse ? 'use' : ''; + const suffix = suffixQueryMutation + ? isQueryType + ? 'Query' + : 'Mutation' + : ''; + return `${prefix}${operationPascalCase}${suffix}`; +}; + +const parametersPluralizedNames = [ + 'query', + 'path', + 'headers', + 'body', + 'cookies', +] as const; +type ParamNames = (typeof parametersPluralizedNames)[number]; +// Define a conditional type to transform the names +type NonPluralizedName = T extends 'headers' + ? 'header' + : T extends 'cookies' + ? 'cookie' + : T; +function getNonPluralizedName( + name: T, +): NonPluralizedName { + return ( + ['headers', 'cookies'].includes(name) ? name.slice(0, -1) : name + ) as NonPluralizedName; +} +type DataKeyNames = Exclude; +function getDataSubType(identifier: string, dataKey: DataKeyNames) { + return tsc.indexedAccessTypeNode({ + indexType: tsc.literalTypeNode({ + literal: tsc.stringLiteral({ + text: dataKey, + }), + }), + objectType: tsc.typeReferenceNode({ + typeName: identifier, + }), + }); +} + +function createParameterConst( + name: ParamNames, + operation?: IR.OperationObject, +) { + const nonPluralizedName = getNonPluralizedName(name); + if (nonPluralizedName === 'body' && !operation?.body?.schema) return []; + if ( + nonPluralizedName !== 'body' && + !operation?.parameters?.[nonPluralizedName] + ) + return []; + return [ + tsc.constVariable({ + expression: tsc.callExpression({ + functionName: 'toRef', + parameters: [getParameterQualifiedName(name)], + }), + name: `${name}Ref`, + }), + ]; +} +function getParameterQualifiedName(name: ParamNames) { + return tsc.propertyAccessExpression({ + expression: 'params', + isOptional: true, + name, + }); +} +/** + * Creates a composable function for an operation + */ +export const createComposable = ({ + file, + isQuery, + operation, + plugin, +}: { + context: IR.Context; + file: GeneratedFile; + isQuery: boolean; + operation: IR.OperationObject; + plugin: PiniaColadaPlugin['Instance']; +}) => { + // Import necessary functions and types + file.import({ + module: '@pinia/colada', + name: isQuery ? 'useQuery' : 'useMutation', + }); + file.import({ + asType: true, + module: '@pinia/colada', + name: `Use${isQuery ? 'Query' : 'Mutation'}Options`, + }); + file.import({ + module: 'vue', + name: 'toRef', + }); + + // Get query key from hooks or generate default + const queryKey = plugin.config.resolveQueryKey?.(operation) ?? [ + operation.tags?.[0] || 'default', + operation.id, + ]; + + // Get identifiers for data, response and error types + const identifierData = importIdentifierData(); + const identifierResponse = importIdentifierResponse(); + const identifierError = importIdentifierError(); + + /** + * Creates a parameter for a composable function + */ + function createParameter( + name: ParamNames, + operation?: IR.OperationObject, + ): Array { + const nonPluralizedName = getNonPluralizedName(name); + if (nonPluralizedName === 'body' && !operation?.body?.schema) return []; + if ( + nonPluralizedName !== 'body' && + !operation?.parameters?.[nonPluralizedName] + ) + return []; + let type: TypeNode = tsc.keywordTypeNode({ keyword: 'unknown' }); + if (nonPluralizedName === 'cookie') { + type = + schemaToType({ + onRef: undefined, + plugin: plugin as any, + schema: irParametersToIrSchema({ + parameters: operation?.parameters?.cookie || {}, + }), + state: { + usedTypeIDs: new Set(), + }, + }) ?? type; + } else if (name !== 'cookies') { + type = identifierData.name + ? getDataSubType(identifierData.name, name) + : type; + } + return [ + { + name, + type, + }, + ]; + } + const parameters = parametersPluralizedNames.flatMap((name) => + createParameter(name, operation), + ); + + // Create the composable function + const node = tsc.constVariable({ + comment: [ + operation.deprecated && '@deprecated', + operation.summary && escapeComment(operation.summary), + operation.description && escapeComment(operation.description), + ].filter(Boolean), + exportConst: true, + expression: tsc.arrowFunction({ + async: true, + parameters: [ + { + isRequired: parameters.length > 0, + name: 'params', + type: tsc.typeInterfaceNode({ + properties: parameters, + useLegacyResolution: true, + }), + }, + // Additional Pinia Colada options + { + isRequired: false, + name: 'options', + type: tsc.typeReferenceNode({ + typeName: isQuery + ? `UseQueryOptions<${identifierResponse.name || 'unknown'}, ${identifierError.name || 'unknown'}, ${identifierData.name || 'unknown'}>` + : `UseMutationOptions<${identifierResponse.name || 'unknown'}, ${identifierData.name || 'unknown'}, ${identifierError.name || 'unknown'}>`, + }), + }, + ], + statements: [ + // Create reactive refs for parameters + ...parametersPluralizedNames.flatMap((name) => + createParameterConst(name, operation), + ), + + // Create query/mutation result + tsc.constVariable({ + expression: tsc.callExpression({ + functionName: isQuery ? 'useQuery' : 'useMutation', + parameters: [ + tsc.objectExpression({ + obj: [ + // Query/mutation function + { + key: isQuery ? 'query' : 'mutation', + value: tsc.callExpression({ + functionName: '_heyApiClient', + parameters: [ + tsc.objectExpression({ + obj: [ + { + key: 'method', + value: operation.method, + }, + { + key: 'url', + value: operation.path, + }, + // Add data if it's a valid body parameter (mutations only) + ...parametersPluralizedNames.flatMap((name) => { + const nonPluralizedName = + getNonPluralizedName(name); + if ( + nonPluralizedName === 'body' && + !operation?.body?.schema + ) + return []; + if ( + nonPluralizedName !== 'body' && + !operation?.parameters?.[nonPluralizedName] + ) + return []; + return [ + { + key: + nonPluralizedName === 'body' + ? 'data' + : name, + value: tsc.identifier({ text: `${name}Ref` }), + }, + ]; + }), + ].filter(Boolean), + }), + ], + }), + }, + // Query key (optional for mutations) + { + key: 'key', + value: tsc.arrayLiteralExpression({ + elements: [ + ...queryKey.map((k: string) => tsc.ots.string(k)), + // Add path params to query key if they exist + ...parametersPluralizedNames.flatMap((name) => { + const nonPluralizedName = getNonPluralizedName(name); + if ( + nonPluralizedName === 'body' && + !operation?.body?.schema + ) + return []; + if ( + nonPluralizedName !== 'body' && + !operation?.parameters?.[nonPluralizedName] + ) + return []; + return [tsc.identifier({ text: `${name}Ref` })]; + }), + ], + }), + }, + // Spread additional options + { + spread: 'options', + }, + ], + }), + ], + }), + name: isQuery ? 'queryResult' : 'mutationResult', + }), + + // Return useQuery/useMutation call with reactive parameters + tsc.returnStatement({ + expression: tsc.objectExpression({ + obj: [ + // Spread the query/mutation result + { + spread: isQuery ? 'queryResult' : 'mutationResult', + }, + // Return reactive parameters + ...parametersPluralizedNames.flatMap((name) => { + const nonPluralizedName = getNonPluralizedName(name); + if (nonPluralizedName === 'body' && !operation?.body?.schema) + return []; + if ( + nonPluralizedName !== 'body' && + !operation?.parameters?.[nonPluralizedName] + ) + return []; + return [ + { + key: name, + value: tsc.identifier({ text: `${name}Ref` }), + }, + ]; + }), + ], + }), + }), + ], + }), + name: generateFunctionName( + operation, + isQuery, + plugin.config.prefixUse, + plugin.config.suffixQueryMutation, + ), + }); + + file.add(node); +}; diff --git a/packages/openapi-ts/src/plugins/config.ts b/packages/openapi-ts/src/plugins/config.ts index 1f016fc697..8ae31f780d 100644 --- a/packages/openapi-ts/src/plugins/config.ts +++ b/packages/openapi-ts/src/plugins/config.ts @@ -28,6 +28,8 @@ import type { HeyApiTransformersPlugin } from './@hey-api/transformers'; import { defaultConfig as heyApiTransformers } from './@hey-api/transformers'; import type { HeyApiTypeScriptPlugin } from './@hey-api/typescript'; import { defaultConfig as heyApiTypeScript } from './@hey-api/typescript'; +import type { PiniaColadaPlugin } from './@pinia/colada'; +import { defaultConfig as piniaColada } from './@pinia/colada'; import type { TanStackAngularQueryPlugin } from './@tanstack/angular-query-experimental'; import { defaultConfig as tanStackAngularQuery } from './@tanstack/angular-query-experimental'; import type { TanStackReactQueryPlugin } from './@tanstack/react-query'; @@ -57,6 +59,7 @@ export interface PluginConfigMap { '@hey-api/sdk': HeyApiSdkPlugin['Types']; '@hey-api/transformers': HeyApiTransformersPlugin['Types']; '@hey-api/typescript': HeyApiTypeScriptPlugin['Types']; + '@pinia/colada': PiniaColadaPlugin['Types']; '@tanstack/angular-query-experimental': TanStackAngularQueryPlugin['Types']; '@tanstack/react-query': TanStackReactQueryPlugin['Types']; '@tanstack/solid-query': TanStackSolidQueryPlugin['Types']; @@ -85,6 +88,7 @@ export const defaultPluginConfigs: { '@hey-api/sdk': heyApiSdk, '@hey-api/transformers': heyApiTransformers, '@hey-api/typescript': heyApiTypeScript, + '@pinia/colada': piniaColada, '@tanstack/angular-query-experimental': tanStackAngularQuery, '@tanstack/react-query': tanStackReactQuery, '@tanstack/solid-query': tanStackSolidQuery, diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 2af13ad8b2..1e2ecf882d 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -26,6 +26,7 @@ export type PluginNames = | '@hey-api/sdk' | '@hey-api/transformers' | '@hey-api/typescript' + | '@pinia/colada' | '@tanstack/angular-query-experimental' | '@tanstack/react-query' | '@tanstack/solid-query'