fork of hey-api/openapi-ts because I need some additional things

chore: fix tests

Lubos ​ cfc0abb4 29605a0e

+1571 -111
-5
.changeset/famous-penguins-repair.md
··· 1 - --- 2 - '@hey-api/nuxt': minor 3 - --- 4 - 5 - feat: remove `@hey-api/client-nuxt` dependency
+4
.gitignore
··· 24 24 25 25 # error files 26 26 openapi-ts-error-* 27 + 28 + # But DO NOT ignore generated snapshots! 29 + !test/__snapshots__/test/generated 30 + !test/__snapshots__/test/generated/**
+1 -1
examples/openapi-ts-axios/tsconfig.json
··· 17 17 /* Linting */ 18 18 "strict": true, 19 19 "noUnusedLocals": true, 20 - "noUnusedParameters": true, 20 + "noUnusedParameters": false, 21 21 "noFallthroughCasesInSwitch": true 22 22 }, 23 23 "include": ["src"],
+1 -1
examples/openapi-ts-fastify/tsconfig.json
··· 1 1 { 2 2 "include": ["src/**/*", "test/**/*"], 3 3 "compilerOptions": { 4 - "lib": ["es2023"], 4 + "lib": ["es2023", "dom", "dom.iterable"], 5 5 "target": "es2022", 6 6 "module": "ESNext", 7 7 "moduleResolution": "Bundler",
+1 -1
examples/openapi-ts-fetch/tsconfig.json
··· 17 17 /* Linting */ 18 18 "strict": true, 19 19 "noUnusedLocals": true, 20 - "noUnusedParameters": true, 20 + "noUnusedParameters": false, 21 21 "noFallthroughCasesInSwitch": true 22 22 }, 23 23 "include": ["src"],
+1 -1
examples/openapi-ts-tanstack-react-query/tsconfig.json
··· 17 17 /* Linting */ 18 18 "strict": true, 19 19 "noUnusedLocals": true, 20 - "noUnusedParameters": true, 20 + "noUnusedParameters": false, 21 21 "noFallthroughCasesInSwitch": true 22 22 }, 23 23 "include": ["src"],
+3 -2
examples/openapi-ts-tanstack-vue-query/src/views/TanstackExample.vue
··· 1 1 <script lang="ts" setup> 2 2 import type { Pet } from '@/client' 3 - import { createClient } from '@client/client' 3 + import { createClient } from '@/client/client' 4 4 import { PetSchema } from '@/client/schemas.gen' 5 5 import { 6 6 addPetMutation, ··· 9 9 } from '@/client/@tanstack/vue-query.gen' 10 10 import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query' 11 11 import { computed, ref, watch } from 'vue' 12 + import type { RequestOptions } from '@/client/client' 12 13 13 14 const queryClient = useQueryClient() 14 15 ··· 25 26 } 26 27 }) 27 28 28 - localClient.interceptors.request.use((request, options) => { 29 + localClient.interceptors.request.use((request: Request, options: RequestOptions) => { 29 30 // Middleware is great for adding authorization tokens to requests made to 30 31 // protected paths. Headers are set randomly here to allow surfacing the 31 32 // default headers, too.
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 3 import type { ClientOptions } from './types.gen.js'; 4 - import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client.js'; 4 + import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client/index.js'; 5 5 6 6 /** 7 7 * The `createClientConfig()` function will be called on client initialization
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/client.ts
··· 1 1 import type { AxiosError, RawAxiosRequestHeaders } from 'axios'; 2 2 import axios from 'axios'; 3 3 4 - import type { Client, Config } from './types'; 4 + import type { Client, Config } from './types.js'; 5 5 import { 6 6 buildUrl, 7 7 createConfig, 8 8 mergeConfigs, 9 9 mergeHeaders, 10 10 setAuthParams, 11 - } from './utils'; 11 + } from './utils.js'; 12 12 13 13 export const createClient = (config: Config = {}): Client => { 14 14 let _config = mergeConfigs(createConfig(), config);
+7 -7
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/index.ts
··· 1 - export type { Auth } from '../core/auth'; 2 - export type { QuerySerializerOptions } from '../core/bodySerializer'; 1 + export type { Auth } from '../core/auth.js'; 2 + export type { QuerySerializerOptions } from '../core/bodySerializer.js'; 3 3 export { 4 4 formDataBodySerializer, 5 5 jsonBodySerializer, 6 6 urlSearchParamsBodySerializer, 7 - } from '../core/bodySerializer'; 8 - export { buildClientParams } from '../core/params'; 9 - export { createClient } from './client'; 7 + } from '../core/bodySerializer.js'; 8 + export { buildClientParams } from '../core/params.js'; 9 + export { createClient } from './client.js'; 10 10 export type { 11 11 Client, 12 12 ClientOptions, ··· 17 17 RequestOptions, 18 18 RequestResult, 19 19 TDataShape, 20 - } from './types'; 21 - export { createConfig } from './utils'; 20 + } from './types.js'; 21 + export { createConfig } from './utils.js';
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/types.ts
··· 6 6 CreateAxiosDefaults, 7 7 } from 'axios'; 8 8 9 - import type { Auth } from '../core/auth'; 9 + import type { Auth } from '../core/auth.js'; 10 10 import type { 11 11 Client as CoreClient, 12 12 Config as CoreConfig, 13 - } from '../core/types'; 13 + } from '../core/types.js'; 14 14 15 15 export interface Config<T extends ClientOptions = ClientOptions> 16 16 extends Omit<CreateAxiosDefaults, 'auth' | 'baseURL' | 'headers' | 'method'>,
+5 -5
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/utils.ts
··· 1 - import { getAuthToken } from '../core/auth'; 1 + import { getAuthToken } from '../core/auth.js'; 2 2 import type { 3 3 QuerySerializer, 4 4 QuerySerializerOptions, 5 - } from '../core/bodySerializer'; 6 - import type { ArraySeparatorStyle } from '../core/pathSerializer'; 5 + } from '../core/bodySerializer.js'; 6 + import type { ArraySeparatorStyle } from '../core/pathSerializer.js'; 7 7 import { 8 8 serializeArrayParam, 9 9 serializeObjectParam, 10 10 serializePrimitiveParam, 11 - } from '../core/pathSerializer'; 12 - import type { Client, ClientOptions, Config, RequestOptions } from './types'; 11 + } from '../core/pathSerializer.js'; 12 + import type { Client, ClientOptions, Config, RequestOptions } from './types.js'; 13 13 14 14 interface PathSerializer { 15 15 path: Record<string, unknown>;
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/core/bodySerializer.ts
··· 2 2 ArrayStyle, 3 3 ObjectStyle, 4 4 SerializerOptions, 5 - } from './pathSerializer'; 5 + } from './pathSerializer.js'; 6 6 7 7 export type QuerySerializer = (query: Record<string, unknown>) => string; 8 8
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/core/types.ts
··· 1 - import type { Auth, AuthToken } from './auth'; 1 + import type { Auth, AuthToken } from './auth.js'; 2 2 import type { 3 3 BodySerializer, 4 4 QuerySerializer, 5 5 QuerySerializerOptions, 6 - } from './bodySerializer'; 6 + } from './bodySerializer.js'; 7 7 8 8 export interface Client< 9 9 RequestFn = never,
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/sdk.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client.js'; 3 + import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client/index.js'; 4 4 import type { ExportData, PatchApiVbyApiVersionNoTagData, PatchApiVbyApiVersionNoTagResponses, ImportData, ImportResponses, FooWowData, FooWowResponses, ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponses, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationResponses, GetApiVbyApiVersionSimpleOperationErrors, DeleteCallWithoutParametersAndResponseData, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, OptionsCallWithoutParametersAndResponseData, PatchCallWithoutParametersAndResponseData, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponses, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, DuplicateNameData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, CallWithNoContentResponseData, CallWithNoContentResponseResponses, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponses, DummyAData, DummyAResponses, DummyBData, DummyBResponses, CallWithResponseData, CallWithResponseResponses, CallWithDuplicateResponsesData, CallWithDuplicateResponsesResponses, CallWithDuplicateResponsesErrors, CallWithResponsesData, CallWithResponsesResponses, CallWithResponsesErrors, CollectionFormatData, TypesData, TypesResponses, UploadFileData, UploadFileResponses, FileResponseData, FileResponseResponses, ComplexTypesData, ComplexTypesResponses, ComplexTypesErrors, MultipartResponseData, MultipartResponseResponses, MultipartRequestData, ComplexParamsData, ComplexParamsResponses, CallWithResultFromHeaderData, CallWithResultFromHeaderResponses, CallWithResultFromHeaderErrors, TestErrorCodeData, TestErrorCodeResponses, TestErrorCodeErrors, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Responses, PutWithFormUrlEncodedData } from './types.gen.js'; 5 5 import { client as _heyApiClient } from './client.gen.js'; 6 6
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 3 import type { ClientOptions } from './types.gen.js'; 4 - import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client.js'; 4 + import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client/index.js'; 5 5 6 6 /** 7 7 * The `createClientConfig()` function will be called on client initialization
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/client.ts
··· 1 - import type { Client, Config, RequestOptions } from './types'; 1 + import type { Client, Config, RequestOptions } from './types.js'; 2 2 import { 3 3 buildUrl, 4 4 createConfig, ··· 7 7 mergeConfigs, 8 8 mergeHeaders, 9 9 setAuthParams, 10 - } from './utils'; 10 + } from './utils.js'; 11 11 12 12 type ReqInit = Omit<RequestInit, 'body' | 'headers'> & { 13 13 body?: any;
+7 -7
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/index.ts
··· 1 - export type { Auth } from '../core/auth'; 2 - export type { QuerySerializerOptions } from '../core/bodySerializer'; 1 + export type { Auth } from '../core/auth.js'; 2 + export type { QuerySerializerOptions } from '../core/bodySerializer.js'; 3 3 export { 4 4 formDataBodySerializer, 5 5 jsonBodySerializer, 6 6 urlSearchParamsBodySerializer, 7 - } from '../core/bodySerializer'; 8 - export { buildClientParams } from '../core/params'; 9 - export { createClient } from './client'; 7 + } from '../core/bodySerializer.js'; 8 + export { buildClientParams } from '../core/params.js'; 9 + export { createClient } from './client.js'; 10 10 export type { 11 11 Client, 12 12 ClientOptions, ··· 18 18 RequestResult, 19 19 ResponseStyle, 20 20 TDataShape, 21 - } from './types'; 22 - export { createConfig, mergeHeaders } from './utils'; 21 + } from './types.js'; 22 + export { createConfig, mergeHeaders } from './utils.js';
+3 -3
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/types.ts
··· 1 - import type { Auth } from '../core/auth'; 1 + import type { Auth } from '../core/auth.js'; 2 2 import type { 3 3 Client as CoreClient, 4 4 Config as CoreConfig, 5 - } from '../core/types'; 6 - import type { Middleware } from './utils'; 5 + } from '../core/types.js'; 6 + import type { Middleware } from './utils.js'; 7 7 8 8 export type ResponseStyle = 'data' | 'fields'; 9 9
+5 -5
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/utils.ts
··· 1 - import { getAuthToken } from '../core/auth'; 1 + import { getAuthToken } from '../core/auth.js'; 2 2 import type { 3 3 QuerySerializer, 4 4 QuerySerializerOptions, 5 - } from '../core/bodySerializer'; 6 - import { jsonBodySerializer } from '../core/bodySerializer'; 5 + } from '../core/bodySerializer.js'; 6 + import { jsonBodySerializer } from '../core/bodySerializer.js'; 7 7 import { 8 8 serializeArrayParam, 9 9 serializeObjectParam, 10 10 serializePrimitiveParam, 11 - } from '../core/pathSerializer'; 12 - import type { Client, ClientOptions, Config, RequestOptions } from './types'; 11 + } from '../core/pathSerializer.js'; 12 + import type { Client, ClientOptions, Config, RequestOptions } from './types.js'; 13 13 14 14 interface PathSerializer { 15 15 path: Record<string, unknown>;
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/core/bodySerializer.ts
··· 2 2 ArrayStyle, 3 3 ObjectStyle, 4 4 SerializerOptions, 5 - } from './pathSerializer'; 5 + } from './pathSerializer.js'; 6 6 7 7 export type QuerySerializer = (query: Record<string, unknown>) => string; 8 8
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/core/types.ts
··· 1 - import type { Auth, AuthToken } from './auth'; 1 + import type { Auth, AuthToken } from './auth.js'; 2 2 import type { 3 3 BodySerializer, 4 4 QuerySerializer, 5 5 QuerySerializerOptions, 6 - } from './bodySerializer'; 6 + } from './bodySerializer.js'; 7 7 8 8 export interface Client< 9 9 RequestFn = never,
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/sdk.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client.js'; 3 + import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client/index.js'; 4 4 import type { ExportData, PatchApiVbyApiVersionNoTagData, PatchApiVbyApiVersionNoTagResponses, ImportData, ImportResponses, FooWowData, FooWowResponses, ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponses, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationResponses, GetApiVbyApiVersionSimpleOperationErrors, DeleteCallWithoutParametersAndResponseData, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, OptionsCallWithoutParametersAndResponseData, PatchCallWithoutParametersAndResponseData, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponses, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, DuplicateNameData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, CallWithNoContentResponseData, CallWithNoContentResponseResponses, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponses, DummyAData, DummyAResponses, DummyBData, DummyBResponses, CallWithResponseData, CallWithResponseResponses, CallWithDuplicateResponsesData, CallWithDuplicateResponsesResponses, CallWithDuplicateResponsesErrors, CallWithResponsesData, CallWithResponsesResponses, CallWithResponsesErrors, CollectionFormatData, TypesData, TypesResponses, UploadFileData, UploadFileResponses, FileResponseData, FileResponseResponses, ComplexTypesData, ComplexTypesResponses, ComplexTypesErrors, MultipartResponseData, MultipartResponseResponses, MultipartRequestData, ComplexParamsData, ComplexParamsResponses, CallWithResultFromHeaderData, CallWithResultFromHeaderResponses, CallWithResultFromHeaderErrors, TestErrorCodeData, TestErrorCodeResponses, TestErrorCodeErrors, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Responses, PutWithFormUrlEncodedData } from './types.gen.js'; 5 5 import { client as _heyApiClient } from './client.gen.js'; 6 6
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 3 import type { ClientOptions } from './types.gen.js'; 4 - import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client.js'; 4 + import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client/index.js'; 5 5 6 6 /** 7 7 * The `createClientConfig()` function will be called on client initialization
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/client.ts
··· 1 - import type { Client, Config, RequestOptions } from './types'; 1 + import type { Client, Config, RequestOptions } from './types.js'; 2 2 import { 3 3 buildUrl, 4 4 createConfig, ··· 7 7 mergeConfigs, 8 8 mergeHeaders, 9 9 setAuthParams, 10 - } from './utils'; 10 + } from './utils.js'; 11 11 12 12 type ReqInit = Omit<RequestInit, 'body' | 'headers'> & { 13 13 body?: any;
+7 -7
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/index.ts
··· 1 - export type { Auth } from '../core/auth'; 2 - export type { QuerySerializerOptions } from '../core/bodySerializer'; 1 + export type { Auth } from '../core/auth.js'; 2 + export type { QuerySerializerOptions } from '../core/bodySerializer.js'; 3 3 export { 4 4 formDataBodySerializer, 5 5 jsonBodySerializer, 6 6 urlSearchParamsBodySerializer, 7 - } from '../core/bodySerializer'; 8 - export { buildClientParams } from '../core/params'; 9 - export { createClient } from './client'; 7 + } from '../core/bodySerializer.js'; 8 + export { buildClientParams } from '../core/params.js'; 9 + export { createClient } from './client.js'; 10 10 export type { 11 11 Client, 12 12 ClientOptions, ··· 17 17 RequestOptions, 18 18 RequestResult, 19 19 TDataShape, 20 - } from './types'; 21 - export { createConfig } from './utils'; 20 + } from './types.js'; 21 + export { createConfig } from './utils.js';
+3 -3
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/types.ts
··· 1 - import type { Auth } from '../core/auth'; 1 + import type { Auth } from '../core/auth.js'; 2 2 import type { 3 3 Client as CoreClient, 4 4 Config as CoreConfig, 5 - } from '../core/types'; 6 - import type { Middleware } from './utils'; 5 + } from '../core/types.js'; 6 + import type { Middleware } from './utils.js'; 7 7 8 8 export interface Config<T extends ClientOptions = ClientOptions> 9 9 extends Omit<RequestInit, 'body' | 'headers' | 'method'>,
+5 -5
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/utils.ts
··· 1 - import { getAuthToken } from '../core/auth'; 1 + import { getAuthToken } from '../core/auth.js'; 2 2 import type { 3 3 QuerySerializer, 4 4 QuerySerializerOptions, 5 - } from '../core/bodySerializer'; 6 - import { jsonBodySerializer } from '../core/bodySerializer'; 5 + } from '../core/bodySerializer.js'; 6 + import { jsonBodySerializer } from '../core/bodySerializer.js'; 7 7 import { 8 8 serializeArrayParam, 9 9 serializeObjectParam, 10 10 serializePrimitiveParam, 11 - } from '../core/pathSerializer'; 12 - import type { Client, ClientOptions, Config, RequestOptions } from './types'; 11 + } from '../core/pathSerializer.js'; 12 + import type { Client, ClientOptions, Config, RequestOptions } from './types.js'; 13 13 14 14 interface PathSerializer { 15 15 path: Record<string, unknown>;
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/core/bodySerializer.ts
··· 2 2 ArrayStyle, 3 3 ObjectStyle, 4 4 SerializerOptions, 5 - } from './pathSerializer'; 5 + } from './pathSerializer.js'; 6 6 7 7 export type QuerySerializer = (query: Record<string, unknown>) => string; 8 8
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/core/types.ts
··· 1 - import type { Auth, AuthToken } from './auth'; 1 + import type { Auth, AuthToken } from './auth.js'; 2 2 import type { 3 3 BodySerializer, 4 4 QuerySerializer, 5 5 QuerySerializerOptions, 6 - } from './bodySerializer'; 6 + } from './bodySerializer.js'; 7 7 8 8 export interface Client< 9 9 RequestFn = never,
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/sdk.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client.js'; 3 + import { type Options as ClientOptions, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client/index.js'; 4 4 import type { ExportData, PatchApiVbyApiVersionNoTagData, PatchApiVbyApiVersionNoTagResponses, ImportData, ImportResponses, FooWowData, FooWowResponses, ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponses, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationResponses, GetApiVbyApiVersionSimpleOperationErrors, DeleteCallWithoutParametersAndResponseData, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, OptionsCallWithoutParametersAndResponseData, PatchCallWithoutParametersAndResponseData, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponses, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, DuplicateNameData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, CallWithNoContentResponseData, CallWithNoContentResponseResponses, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponses, DummyAData, DummyAResponses, DummyBData, DummyBResponses, CallWithResponseData, CallWithResponseResponses, CallWithDuplicateResponsesData, CallWithDuplicateResponsesResponses, CallWithDuplicateResponsesErrors, CallWithResponsesData, CallWithResponsesResponses, CallWithResponsesErrors, CollectionFormatData, TypesData, TypesResponses, UploadFileData, UploadFileResponses, FileResponseData, FileResponseResponses, ComplexTypesData, ComplexTypesResponses, ComplexTypesErrors, MultipartResponseData, MultipartResponseResponses, MultipartRequestData, ComplexParamsData, ComplexParamsResponses, CallWithResultFromHeaderData, CallWithResultFromHeaderResponses, CallWithResultFromHeaderErrors, TestErrorCodeData, TestErrorCodeResponses, TestErrorCodeErrors, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Responses, PutWithFormUrlEncodedData } from './types.gen.js'; 5 5 import { client as _heyApiClient } from './client.gen.js'; 6 6
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 3 import type { ClientOptions } from './types.gen.js'; 4 - import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client.js'; 4 + import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client/index.js'; 5 5 6 6 /** 7 7 * The `createClientConfig()` function will be called on client initialization
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/client.ts
··· 6 6 } from 'nuxt/app'; 7 7 import { reactive, ref, watch } from 'vue'; 8 8 9 - import type { Client, Config } from './types'; 9 + import type { Client, Config } from './types.js'; 10 10 import { 11 11 buildUrl, 12 12 createConfig, ··· 16 16 mergeInterceptors, 17 17 serializeBody, 18 18 setAuthParams, 19 - } from './utils'; 19 + } from './utils.js'; 20 20 21 21 export const createClient = (config: Config = {}): Client => { 22 22 let _config = mergeConfigs(createConfig(), config);
+7 -7
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/index.ts
··· 1 - export type { Auth } from '../core/auth'; 2 - export type { QuerySerializerOptions } from '../core/bodySerializer'; 1 + export type { Auth } from '../core/auth.js'; 2 + export type { QuerySerializerOptions } from '../core/bodySerializer.js'; 3 3 export { 4 4 formDataBodySerializer, 5 5 jsonBodySerializer, 6 6 urlSearchParamsBodySerializer, 7 - } from '../core/bodySerializer'; 8 - export { buildClientParams } from '../core/params'; 9 - export { createClient } from './client'; 7 + } from '../core/bodySerializer.js'; 8 + export { buildClientParams } from '../core/params.js'; 9 + export { createClient } from './client.js'; 10 10 export type { 11 11 Client, 12 12 ClientOptions, ··· 18 18 RequestOptions, 19 19 RequestResult, 20 20 TDataShape, 21 - } from './types'; 22 - export { createConfig } from './utils'; 21 + } from './types.js'; 22 + export { createConfig } from './utils.js';
+3 -3
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/types.ts
··· 8 8 } from 'nuxt/app'; 9 9 import type { Ref } from 'vue'; 10 10 11 - import type { Auth } from '../core/auth'; 12 - import type { QuerySerializerOptions } from '../core/bodySerializer'; 11 + import type { Auth } from '../core/auth.js'; 12 + import type { QuerySerializerOptions } from '../core/bodySerializer.js'; 13 13 import type { 14 14 Client as CoreClient, 15 15 Config as CoreConfig, 16 - } from '../core/types'; 16 + } from '../core/types.js'; 17 17 18 18 export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; 19 19 type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
+5 -5
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/utils.ts
··· 1 1 import type { ComputedRef, Ref } from 'vue'; 2 2 import { isRef, toValue, unref } from 'vue'; 3 3 4 - import { getAuthToken } from '../core/auth'; 5 - import type { QuerySerializerOptions } from '../core/bodySerializer'; 6 - import { jsonBodySerializer } from '../core/bodySerializer'; 4 + import { getAuthToken } from '../core/auth.js'; 5 + import type { QuerySerializerOptions } from '../core/bodySerializer.js'; 6 + import { jsonBodySerializer } from '../core/bodySerializer.js'; 7 7 import { 8 8 serializeArrayParam, 9 9 serializeObjectParam, 10 10 serializePrimitiveParam, 11 - } from '../core/pathSerializer'; 11 + } from '../core/pathSerializer.js'; 12 12 import type { 13 13 ArraySeparatorStyle, 14 14 BuildUrlOptions, ··· 17 17 Config, 18 18 QuerySerializer, 19 19 RequestOptions, 20 - } from './types'; 20 + } from './types.js'; 21 21 22 22 type PathSerializer = Pick<Required<BuildUrlOptions>, 'path' | 'url'>; 23 23
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/core/bodySerializer.ts
··· 2 2 ArrayStyle, 3 3 ObjectStyle, 4 4 SerializerOptions, 5 - } from './pathSerializer'; 5 + } from './pathSerializer.js'; 6 6 7 7 export type QuerySerializer = (query: Record<string, unknown>) => string; 8 8
+2 -2
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/core/types.ts
··· 1 - import type { Auth, AuthToken } from './auth'; 1 + import type { Auth, AuthToken } from './auth.js'; 2 2 import type { 3 3 BodySerializer, 4 4 QuerySerializer, 5 5 QuerySerializerOptions, 6 - } from './bodySerializer'; 6 + } from './bodySerializer.js'; 7 7 8 8 export interface Client< 9 9 RequestFn = never,
+1 -1
packages/openapi-ts-tests/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/sdk.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { type Options as ClientOptions, type Composable, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client.js'; 3 + import { type Options as ClientOptions, type Composable, type TDataShape, type Client, formDataBodySerializer, urlSearchParamsBodySerializer } from './client/index.js'; 4 4 import type { ExportData, PatchApiVbyApiVersionNoTagData, ImportData, ImportResponse, FooWowData, ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponse, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationResponse, GetApiVbyApiVersionSimpleOperationError, DeleteCallWithoutParametersAndResponseData, GetCallWithoutParametersAndResponseData, HeadCallWithoutParametersAndResponseData, OptionsCallWithoutParametersAndResponseData, PatchCallWithoutParametersAndResponseData, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, DuplicateNameData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, CallWithNoContentResponseData, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, DummyAData, DummyAResponse, DummyBData, DummyBResponse, CallWithResponseData, CallWithResponseResponse, CallWithDuplicateResponsesData, CallWithDuplicateResponsesResponse, CallWithDuplicateResponsesError, CallWithResponsesData, CallWithResponsesResponse, CallWithResponsesError, CollectionFormatData, TypesData, TypesResponse, UploadFileData, UploadFileResponse, FileResponseData, FileResponseResponse, ComplexTypesData, ComplexTypesResponse, MultipartResponseData, MultipartResponseResponse, MultipartRequestData, ComplexParamsData, ComplexParamsResponse, CallWithResultFromHeaderData, TestErrorCodeData, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from './types.gen.js'; 5 5 import { client as _heyApiClient } from './client.gen.js'; 6 6
+181
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/client/client.ts.snap
··· 1 + import type { Client, Config, RequestOptions } from './types'; 2 + import { 3 + buildUrl, 4 + createConfig, 5 + createInterceptors, 6 + getParseAs, 7 + mergeConfigs, 8 + mergeHeaders, 9 + setAuthParams, 10 + } from './utils'; 11 + 12 + type ReqInit = Omit<RequestInit, 'body' | 'headers'> & { 13 + body?: any; 14 + headers: ReturnType<typeof mergeHeaders>; 15 + }; 16 + 17 + export const createClient = (config: Config = {}): Client => { 18 + let _config = mergeConfigs(createConfig(), config); 19 + 20 + const getConfig = (): Config => ({ ..._config }); 21 + 22 + const setConfig = (config: Config): Config => { 23 + _config = mergeConfigs(_config, config); 24 + return getConfig(); 25 + }; 26 + 27 + const interceptors = createInterceptors< 28 + Request, 29 + Response, 30 + unknown, 31 + RequestOptions 32 + >(); 33 + 34 + const request: Client['request'] = async (options) => { 35 + const opts = { 36 + ..._config, 37 + ...options, 38 + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, 39 + headers: mergeHeaders(_config.headers, options.headers), 40 + }; 41 + 42 + if (opts.security) { 43 + await setAuthParams({ 44 + ...opts, 45 + security: opts.security, 46 + }); 47 + } 48 + 49 + if (opts.body && opts.bodySerializer) { 50 + opts.body = opts.bodySerializer(opts.body); 51 + } 52 + 53 + // remove Content-Type header if body is empty to avoid sending invalid requests 54 + if (opts.body === undefined || opts.body === '') { 55 + opts.headers.delete('Content-Type'); 56 + } 57 + 58 + const url = buildUrl(opts); 59 + const requestInit: ReqInit = { 60 + redirect: 'follow', 61 + ...opts, 62 + }; 63 + 64 + let request = new Request(url, requestInit); 65 + 66 + for (const fn of interceptors.request._fns) { 67 + if (fn) { 68 + request = await fn(request, opts); 69 + } 70 + } 71 + 72 + // fetch must be assigned here, otherwise it would throw the error: 73 + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation 74 + const _fetch = opts.fetch!; 75 + let response = await _fetch(request); 76 + 77 + for (const fn of interceptors.response._fns) { 78 + if (fn) { 79 + response = await fn(response, request, opts); 80 + } 81 + } 82 + 83 + const result = { 84 + request, 85 + response, 86 + }; 87 + 88 + if (response.ok) { 89 + if ( 90 + response.status === 204 || 91 + response.headers.get('Content-Length') === '0' 92 + ) { 93 + return opts.responseStyle === 'data' 94 + ? {} 95 + : { 96 + data: {}, 97 + ...result, 98 + }; 99 + } 100 + 101 + const parseAs = 102 + (opts.parseAs === 'auto' 103 + ? getParseAs(response.headers.get('Content-Type')) 104 + : opts.parseAs) ?? 'json'; 105 + 106 + if (parseAs === 'stream') { 107 + return opts.responseStyle === 'data' 108 + ? response.body 109 + : { 110 + data: response.body, 111 + ...result, 112 + }; 113 + } 114 + 115 + let data = await response[parseAs](); 116 + if (parseAs === 'json') { 117 + if (opts.responseValidator) { 118 + await opts.responseValidator(data); 119 + } 120 + 121 + if (opts.responseTransformer) { 122 + data = await opts.responseTransformer(data); 123 + } 124 + } 125 + 126 + return opts.responseStyle === 'data' 127 + ? data 128 + : { 129 + data, 130 + ...result, 131 + }; 132 + } 133 + 134 + let error = await response.text(); 135 + 136 + try { 137 + error = JSON.parse(error); 138 + } catch { 139 + // noop 140 + } 141 + 142 + let finalError = error; 143 + 144 + for (const fn of interceptors.error._fns) { 145 + if (fn) { 146 + finalError = (await fn(error, response, request, opts)) as string; 147 + } 148 + } 149 + 150 + finalError = finalError || ({} as string); 151 + 152 + if (opts.throwOnError) { 153 + throw finalError; 154 + } 155 + 156 + // TODO: we probably want to return error and improve types 157 + return opts.responseStyle === 'data' 158 + ? undefined 159 + : { 160 + error: finalError, 161 + ...result, 162 + }; 163 + }; 164 + 165 + return { 166 + buildUrl, 167 + connect: (options) => request({ ...options, method: 'CONNECT' }), 168 + delete: (options) => request({ ...options, method: 'DELETE' }), 169 + get: (options) => request({ ...options, method: 'GET' }), 170 + getConfig, 171 + head: (options) => request({ ...options, method: 'HEAD' }), 172 + interceptors, 173 + options: (options) => request({ ...options, method: 'OPTIONS' }), 174 + patch: (options) => request({ ...options, method: 'PATCH' }), 175 + post: (options) => request({ ...options, method: 'POST' }), 176 + put: (options) => request({ ...options, method: 'PUT' }), 177 + request, 178 + setConfig, 179 + trace: (options) => request({ ...options, method: 'TRACE' }), 180 + }; 181 + };
+22
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/client/index.ts.snap
··· 1 + export type { Auth } from '../core/auth'; 2 + export type { QuerySerializerOptions } from '../core/bodySerializer'; 3 + export { 4 + formDataBodySerializer, 5 + jsonBodySerializer, 6 + urlSearchParamsBodySerializer, 7 + } from '../core/bodySerializer'; 8 + export { buildClientParams } from '../core/params'; 9 + export { createClient } from './client'; 10 + export type { 11 + Client, 12 + ClientOptions, 13 + Config, 14 + CreateClientConfig, 15 + Options, 16 + OptionsLegacyParser, 17 + RequestOptions, 18 + RequestResult, 19 + ResponseStyle, 20 + TDataShape, 21 + } from './types'; 22 + export { createConfig, mergeHeaders } from './utils';
+215
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/client/types.ts.snap
··· 1 + import type { Auth } from '../core/auth'; 2 + import type { 3 + Client as CoreClient, 4 + Config as CoreConfig, 5 + } from '../core/types'; 6 + import type { Middleware } from './utils'; 7 + 8 + export type ResponseStyle = 'data' | 'fields'; 9 + 10 + export interface Config<T extends ClientOptions = ClientOptions> 11 + extends Omit<RequestInit, 'body' | 'headers' | 'method'>, 12 + CoreConfig { 13 + /** 14 + * Base URL for all requests made by this client. 15 + */ 16 + baseUrl?: T['baseUrl']; 17 + /** 18 + * Fetch API implementation. You can use this option to provide a custom 19 + * fetch instance. 20 + * 21 + * @default globalThis.fetch 22 + */ 23 + fetch?: (request: Request) => ReturnType<typeof fetch>; 24 + /** 25 + * Please don't use the Fetch client for Next.js applications. The `next` 26 + * options won't have any effect. 27 + * 28 + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. 29 + */ 30 + next?: never; 31 + /** 32 + * Return the response data parsed in a specified format. By default, `auto` 33 + * will infer the appropriate method from the `Content-Type` response header. 34 + * You can override this behavior with any of the {@link Body} methods. 35 + * Select `stream` if you don't want to parse response data at all. 36 + * 37 + * @default 'auto' 38 + */ 39 + parseAs?: Exclude<keyof Body, 'body' | 'bodyUsed'> | 'auto' | 'stream'; 40 + /** 41 + * Should we return only data or multiple fields (data, error, response, etc.)? 42 + * 43 + * @default 'fields' 44 + */ 45 + responseStyle?: ResponseStyle; 46 + /** 47 + * Throw an error instead of returning it in the response? 48 + * 49 + * @default false 50 + */ 51 + throwOnError?: T['throwOnError']; 52 + } 53 + 54 + export interface RequestOptions< 55 + TResponseStyle extends ResponseStyle = 'fields', 56 + ThrowOnError extends boolean = boolean, 57 + Url extends string = string, 58 + > extends Config<{ 59 + responseStyle: TResponseStyle; 60 + throwOnError: ThrowOnError; 61 + }> { 62 + /** 63 + * Any body that you want to add to your request. 64 + * 65 + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} 66 + */ 67 + body?: unknown; 68 + path?: Record<string, unknown>; 69 + query?: Record<string, unknown>; 70 + /** 71 + * Security mechanism(s) to use for the request. 72 + */ 73 + security?: ReadonlyArray<Auth>; 74 + url: Url; 75 + } 76 + 77 + export type RequestResult< 78 + TData = unknown, 79 + TError = unknown, 80 + ThrowOnError extends boolean = boolean, 81 + TResponseStyle extends ResponseStyle = 'fields', 82 + > = ThrowOnError extends true 83 + ? Promise< 84 + TResponseStyle extends 'data' 85 + ? TData extends Record<string, unknown> 86 + ? TData[keyof TData] 87 + : TData 88 + : { 89 + data: TData extends Record<string, unknown> 90 + ? TData[keyof TData] 91 + : TData; 92 + request: Request; 93 + response: Response; 94 + } 95 + > 96 + : Promise< 97 + TResponseStyle extends 'data' 98 + ? 99 + | (TData extends Record<string, unknown> 100 + ? TData[keyof TData] 101 + : TData) 102 + | undefined 103 + : ( 104 + | { 105 + data: TData extends Record<string, unknown> 106 + ? TData[keyof TData] 107 + : TData; 108 + error: undefined; 109 + } 110 + | { 111 + data: undefined; 112 + error: TError extends Record<string, unknown> 113 + ? TError[keyof TError] 114 + : TError; 115 + } 116 + ) & { 117 + request: Request; 118 + response: Response; 119 + } 120 + >; 121 + 122 + export interface ClientOptions { 123 + baseUrl?: string; 124 + responseStyle?: ResponseStyle; 125 + throwOnError?: boolean; 126 + } 127 + 128 + type MethodFn = < 129 + TData = unknown, 130 + TError = unknown, 131 + ThrowOnError extends boolean = false, 132 + TResponseStyle extends ResponseStyle = 'fields', 133 + >( 134 + options: Omit<RequestOptions<TResponseStyle, ThrowOnError>, 'method'>, 135 + ) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>; 136 + 137 + type RequestFn = < 138 + TData = unknown, 139 + TError = unknown, 140 + ThrowOnError extends boolean = false, 141 + TResponseStyle extends ResponseStyle = 'fields', 142 + >( 143 + options: Omit<RequestOptions<TResponseStyle, ThrowOnError>, 'method'> & 144 + Pick<Required<RequestOptions<TResponseStyle, ThrowOnError>>, 'method'>, 145 + ) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>; 146 + 147 + type BuildUrlFn = < 148 + TData extends { 149 + body?: unknown; 150 + path?: Record<string, unknown>; 151 + query?: Record<string, unknown>; 152 + url: string; 153 + }, 154 + >( 155 + options: Pick<TData, 'url'> & Options<TData>, 156 + ) => string; 157 + 158 + export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn> & { 159 + interceptors: Middleware<Request, Response, unknown, RequestOptions>; 160 + }; 161 + 162 + /** 163 + * The `createClientConfig()` function will be called on client initialization 164 + * and the returned object will become the client's initial configuration. 165 + * 166 + * You may want to initialize your client this way instead of calling 167 + * `setConfig()`. This is useful for example if you're using Next.js 168 + * to ensure your client always has the correct values. 169 + */ 170 + export type CreateClientConfig<T extends ClientOptions = ClientOptions> = ( 171 + override?: Config<ClientOptions & T>, 172 + ) => Config<Required<ClientOptions> & T>; 173 + 174 + export interface TDataShape { 175 + body?: unknown; 176 + headers?: unknown; 177 + path?: unknown; 178 + query?: unknown; 179 + url: string; 180 + } 181 + 182 + type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>; 183 + 184 + export type Options< 185 + TData extends TDataShape = TDataShape, 186 + ThrowOnError extends boolean = boolean, 187 + TResponseStyle extends ResponseStyle = 'fields', 188 + > = OmitKeys< 189 + RequestOptions<TResponseStyle, ThrowOnError>, 190 + 'body' | 'path' | 'query' | 'url' 191 + > & 192 + Omit<TData, 'url'>; 193 + 194 + export type OptionsLegacyParser< 195 + TData = unknown, 196 + ThrowOnError extends boolean = boolean, 197 + TResponseStyle extends ResponseStyle = 'fields', 198 + > = TData extends { body?: any } 199 + ? TData extends { headers?: any } 200 + ? OmitKeys< 201 + RequestOptions<TResponseStyle, ThrowOnError>, 202 + 'body' | 'headers' | 'url' 203 + > & 204 + TData 205 + : OmitKeys<RequestOptions<TResponseStyle, ThrowOnError>, 'body' | 'url'> & 206 + TData & 207 + Pick<RequestOptions<TResponseStyle, ThrowOnError>, 'headers'> 208 + : TData extends { headers?: any } 209 + ? OmitKeys< 210 + RequestOptions<TResponseStyle, ThrowOnError>, 211 + 'headers' | 'url' 212 + > & 213 + TData & 214 + Pick<RequestOptions<TResponseStyle, ThrowOnError>, 'body'> 215 + : OmitKeys<RequestOptions<TResponseStyle, ThrowOnError>, 'url'> & TData;
+415
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/client/utils.ts.snap
··· 1 + import { getAuthToken } from '../core/auth'; 2 + import type { 3 + QuerySerializer, 4 + QuerySerializerOptions, 5 + } from '../core/bodySerializer'; 6 + import { jsonBodySerializer } from '../core/bodySerializer'; 7 + import { 8 + serializeArrayParam, 9 + serializeObjectParam, 10 + serializePrimitiveParam, 11 + } from '../core/pathSerializer'; 12 + import type { Client, ClientOptions, Config, RequestOptions } from './types'; 13 + 14 + interface PathSerializer { 15 + path: Record<string, unknown>; 16 + url: string; 17 + } 18 + 19 + const PATH_PARAM_RE = /\{[^{}]+\}/g; 20 + 21 + type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; 22 + type MatrixStyle = 'label' | 'matrix' | 'simple'; 23 + type ArraySeparatorStyle = ArrayStyle | MatrixStyle; 24 + 25 + const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { 26 + let url = _url; 27 + const matches = _url.match(PATH_PARAM_RE); 28 + if (matches) { 29 + for (const match of matches) { 30 + let explode = false; 31 + let name = match.substring(1, match.length - 1); 32 + let style: ArraySeparatorStyle = 'simple'; 33 + 34 + if (name.endsWith('*')) { 35 + explode = true; 36 + name = name.substring(0, name.length - 1); 37 + } 38 + 39 + if (name.startsWith('.')) { 40 + name = name.substring(1); 41 + style = 'label'; 42 + } else if (name.startsWith(';')) { 43 + name = name.substring(1); 44 + style = 'matrix'; 45 + } 46 + 47 + const value = path[name]; 48 + 49 + if (value === undefined || value === null) { 50 + continue; 51 + } 52 + 53 + if (Array.isArray(value)) { 54 + url = url.replace( 55 + match, 56 + serializeArrayParam({ explode, name, style, value }), 57 + ); 58 + continue; 59 + } 60 + 61 + if (typeof value === 'object') { 62 + url = url.replace( 63 + match, 64 + serializeObjectParam({ 65 + explode, 66 + name, 67 + style, 68 + value: value as Record<string, unknown>, 69 + valueOnly: true, 70 + }), 71 + ); 72 + continue; 73 + } 74 + 75 + if (style === 'matrix') { 76 + url = url.replace( 77 + match, 78 + `;${serializePrimitiveParam({ 79 + name, 80 + value: value as string, 81 + })}`, 82 + ); 83 + continue; 84 + } 85 + 86 + const replaceValue = encodeURIComponent( 87 + style === 'label' ? `.${value as string}` : (value as string), 88 + ); 89 + url = url.replace(match, replaceValue); 90 + } 91 + } 92 + return url; 93 + }; 94 + 95 + export const createQuerySerializer = <T = unknown>({ 96 + allowReserved, 97 + array, 98 + object, 99 + }: QuerySerializerOptions = {}) => { 100 + const querySerializer = (queryParams: T) => { 101 + const search: string[] = []; 102 + if (queryParams && typeof queryParams === 'object') { 103 + for (const name in queryParams) { 104 + const value = queryParams[name]; 105 + 106 + if (value === undefined || value === null) { 107 + continue; 108 + } 109 + 110 + if (Array.isArray(value)) { 111 + const serializedArray = serializeArrayParam({ 112 + allowReserved, 113 + explode: true, 114 + name, 115 + style: 'form', 116 + value, 117 + ...array, 118 + }); 119 + if (serializedArray) search.push(serializedArray); 120 + } else if (typeof value === 'object') { 121 + const serializedObject = serializeObjectParam({ 122 + allowReserved, 123 + explode: true, 124 + name, 125 + style: 'deepObject', 126 + value: value as Record<string, unknown>, 127 + ...object, 128 + }); 129 + if (serializedObject) search.push(serializedObject); 130 + } else { 131 + const serializedPrimitive = serializePrimitiveParam({ 132 + allowReserved, 133 + name, 134 + value: value as string, 135 + }); 136 + if (serializedPrimitive) search.push(serializedPrimitive); 137 + } 138 + } 139 + } 140 + return search.join('&'); 141 + }; 142 + return querySerializer; 143 + }; 144 + 145 + /** 146 + * Infers parseAs value from provided Content-Type header. 147 + */ 148 + export const getParseAs = ( 149 + contentType: string | null, 150 + ): Exclude<Config['parseAs'], 'auto'> => { 151 + if (!contentType) { 152 + // If no Content-Type header is provided, the best we can do is return the raw response body, 153 + // which is effectively the same as the 'stream' option. 154 + return 'stream'; 155 + } 156 + 157 + const cleanContent = contentType.split(';')[0]?.trim(); 158 + 159 + if (!cleanContent) { 160 + return; 161 + } 162 + 163 + if ( 164 + cleanContent.startsWith('application/json') || 165 + cleanContent.endsWith('+json') 166 + ) { 167 + return 'json'; 168 + } 169 + 170 + if (cleanContent === 'multipart/form-data') { 171 + return 'formData'; 172 + } 173 + 174 + if ( 175 + ['application/', 'audio/', 'image/', 'video/'].some((type) => 176 + cleanContent.startsWith(type), 177 + ) 178 + ) { 179 + return 'blob'; 180 + } 181 + 182 + if (cleanContent.startsWith('text/')) { 183 + return 'text'; 184 + } 185 + }; 186 + 187 + export const setAuthParams = async ({ 188 + security, 189 + ...options 190 + }: Pick<Required<RequestOptions>, 'security'> & 191 + Pick<RequestOptions, 'auth' | 'query'> & { 192 + headers: Headers; 193 + }) => { 194 + for (const auth of security) { 195 + const token = await getAuthToken(auth, options.auth); 196 + 197 + if (!token) { 198 + continue; 199 + } 200 + 201 + const name = auth.name ?? 'Authorization'; 202 + 203 + switch (auth.in) { 204 + case 'query': 205 + if (!options.query) { 206 + options.query = {}; 207 + } 208 + options.query[name] = token; 209 + break; 210 + case 'cookie': 211 + options.headers.append('Cookie', `${name}=${token}`); 212 + break; 213 + case 'header': 214 + default: 215 + options.headers.set(name, token); 216 + break; 217 + } 218 + 219 + return; 220 + } 221 + }; 222 + 223 + export const buildUrl: Client['buildUrl'] = (options) => { 224 + const url = getUrl({ 225 + baseUrl: options.baseUrl as string, 226 + path: options.path, 227 + query: options.query, 228 + querySerializer: 229 + typeof options.querySerializer === 'function' 230 + ? options.querySerializer 231 + : createQuerySerializer(options.querySerializer), 232 + url: options.url, 233 + }); 234 + return url; 235 + }; 236 + 237 + export const getUrl = ({ 238 + baseUrl, 239 + path, 240 + query, 241 + querySerializer, 242 + url: _url, 243 + }: { 244 + baseUrl?: string; 245 + path?: Record<string, unknown>; 246 + query?: Record<string, unknown>; 247 + querySerializer: QuerySerializer; 248 + url: string; 249 + }) => { 250 + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; 251 + let url = (baseUrl ?? '') + pathUrl; 252 + if (path) { 253 + url = defaultPathSerializer({ path, url }); 254 + } 255 + let search = query ? querySerializer(query) : ''; 256 + if (search.startsWith('?')) { 257 + search = search.substring(1); 258 + } 259 + if (search) { 260 + url += `?${search}`; 261 + } 262 + return url; 263 + }; 264 + 265 + export const mergeConfigs = (a: Config, b: Config): Config => { 266 + const config = { ...a, ...b }; 267 + if (config.baseUrl?.endsWith('/')) { 268 + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); 269 + } 270 + config.headers = mergeHeaders(a.headers, b.headers); 271 + return config; 272 + }; 273 + 274 + export const mergeHeaders = ( 275 + ...headers: Array<Required<Config>['headers'] | undefined> 276 + ): Headers => { 277 + const mergedHeaders = new Headers(); 278 + for (const header of headers) { 279 + if (!header || typeof header !== 'object') { 280 + continue; 281 + } 282 + 283 + const iterator = 284 + header instanceof Headers ? header.entries() : Object.entries(header); 285 + 286 + for (const [key, value] of iterator) { 287 + if (value === null) { 288 + mergedHeaders.delete(key); 289 + } else if (Array.isArray(value)) { 290 + for (const v of value) { 291 + mergedHeaders.append(key, v as string); 292 + } 293 + } else if (value !== undefined) { 294 + // assume object headers are meant to be JSON stringified, i.e. their 295 + // content value in OpenAPI specification is 'application/json' 296 + mergedHeaders.set( 297 + key, 298 + typeof value === 'object' ? JSON.stringify(value) : (value as string), 299 + ); 300 + } 301 + } 302 + } 303 + return mergedHeaders; 304 + }; 305 + 306 + type ErrInterceptor<Err, Res, Req, Options> = ( 307 + error: Err, 308 + response: Res, 309 + request: Req, 310 + options: Options, 311 + ) => Err | Promise<Err>; 312 + 313 + type ReqInterceptor<Req, Options> = ( 314 + request: Req, 315 + options: Options, 316 + ) => Req | Promise<Req>; 317 + 318 + type ResInterceptor<Res, Req, Options> = ( 319 + response: Res, 320 + request: Req, 321 + options: Options, 322 + ) => Res | Promise<Res>; 323 + 324 + class Interceptors<Interceptor> { 325 + _fns: (Interceptor | null)[]; 326 + 327 + constructor() { 328 + this._fns = []; 329 + } 330 + 331 + clear() { 332 + this._fns = []; 333 + } 334 + 335 + getInterceptorIndex(id: number | Interceptor): number { 336 + if (typeof id === 'number') { 337 + return this._fns[id] ? id : -1; 338 + } else { 339 + return this._fns.indexOf(id); 340 + } 341 + } 342 + exists(id: number | Interceptor) { 343 + const index = this.getInterceptorIndex(id); 344 + return !!this._fns[index]; 345 + } 346 + 347 + eject(id: number | Interceptor) { 348 + const index = this.getInterceptorIndex(id); 349 + if (this._fns[index]) { 350 + this._fns[index] = null; 351 + } 352 + } 353 + 354 + update(id: number | Interceptor, fn: Interceptor) { 355 + const index = this.getInterceptorIndex(id); 356 + if (this._fns[index]) { 357 + this._fns[index] = fn; 358 + return id; 359 + } else { 360 + return false; 361 + } 362 + } 363 + 364 + use(fn: Interceptor) { 365 + this._fns = [...this._fns, fn]; 366 + return this._fns.length - 1; 367 + } 368 + } 369 + 370 + // `createInterceptors()` response, meant for external use as it does not 371 + // expose internals 372 + export interface Middleware<Req, Res, Err, Options> { 373 + error: Pick< 374 + Interceptors<ErrInterceptor<Err, Res, Req, Options>>, 375 + 'eject' | 'use' 376 + >; 377 + request: Pick<Interceptors<ReqInterceptor<Req, Options>>, 'eject' | 'use'>; 378 + response: Pick< 379 + Interceptors<ResInterceptor<Res, Req, Options>>, 380 + 'eject' | 'use' 381 + >; 382 + } 383 + 384 + // do not add `Middleware` as return type so we can use _fns internally 385 + export const createInterceptors = <Req, Res, Err, Options>() => ({ 386 + error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(), 387 + request: new Interceptors<ReqInterceptor<Req, Options>>(), 388 + response: new Interceptors<ResInterceptor<Res, Req, Options>>(), 389 + }); 390 + 391 + const defaultQuerySerializer = createQuerySerializer({ 392 + allowReserved: false, 393 + array: { 394 + explode: true, 395 + style: 'form', 396 + }, 397 + object: { 398 + explode: true, 399 + style: 'deepObject', 400 + }, 401 + }); 402 + 403 + const defaultHeaders = { 404 + 'Content-Type': 'application/json', 405 + }; 406 + 407 + export const createConfig = <T extends ClientOptions = ClientOptions>( 408 + override: Config<Omit<ClientOptions, keyof T> & T> = {}, 409 + ): Config<Omit<ClientOptions, keyof T> & T> => ({ 410 + ...jsonBodySerializer, 411 + headers: defaultHeaders, 412 + parseAs: 'auto', 413 + querySerializer: defaultQuerySerializer, 414 + ...override, 415 + });
+40
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/core/auth.ts.snap
··· 1 + export type AuthToken = string | undefined; 2 + 3 + export interface Auth { 4 + /** 5 + * Which part of the request do we use to send the auth? 6 + * 7 + * @default 'header' 8 + */ 9 + in?: 'header' | 'query' | 'cookie'; 10 + /** 11 + * Header or query parameter name. 12 + * 13 + * @default 'Authorization' 14 + */ 15 + name?: string; 16 + scheme?: 'basic' | 'bearer'; 17 + type: 'apiKey' | 'http'; 18 + } 19 + 20 + export const getAuthToken = async ( 21 + auth: Auth, 22 + callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken, 23 + ): Promise<string | undefined> => { 24 + const token = 25 + typeof callback === 'function' ? await callback(auth) : callback; 26 + 27 + if (!token) { 28 + return; 29 + } 30 + 31 + if (auth.scheme === 'bearer') { 32 + return `Bearer ${token}`; 33 + } 34 + 35 + if (auth.scheme === 'basic') { 36 + return `Basic ${btoa(token)}`; 37 + } 38 + 39 + return token; 40 + };
+84
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/core/bodySerializer.ts.snap
··· 1 + import type { 2 + ArrayStyle, 3 + ObjectStyle, 4 + SerializerOptions, 5 + } from './pathSerializer'; 6 + 7 + export type QuerySerializer = (query: Record<string, unknown>) => string; 8 + 9 + export type BodySerializer = (body: any) => any; 10 + 11 + export interface QuerySerializerOptions { 12 + allowReserved?: boolean; 13 + array?: SerializerOptions<ArrayStyle>; 14 + object?: SerializerOptions<ObjectStyle>; 15 + } 16 + 17 + const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { 18 + if (typeof value === 'string' || value instanceof Blob) { 19 + data.append(key, value); 20 + } else { 21 + data.append(key, JSON.stringify(value)); 22 + } 23 + }; 24 + 25 + const serializeUrlSearchParamsPair = ( 26 + data: URLSearchParams, 27 + key: string, 28 + value: unknown, 29 + ) => { 30 + if (typeof value === 'string') { 31 + data.append(key, value); 32 + } else { 33 + data.append(key, JSON.stringify(value)); 34 + } 35 + }; 36 + 37 + export const formDataBodySerializer = { 38 + bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>( 39 + body: T, 40 + ) => { 41 + const data = new FormData(); 42 + 43 + Object.entries(body).forEach(([key, value]) => { 44 + if (value === undefined || value === null) { 45 + return; 46 + } 47 + if (Array.isArray(value)) { 48 + value.forEach((v) => serializeFormDataPair(data, key, v)); 49 + } else { 50 + serializeFormDataPair(data, key, value); 51 + } 52 + }); 53 + 54 + return data; 55 + }, 56 + }; 57 + 58 + export const jsonBodySerializer = { 59 + bodySerializer: <T>(body: T) => 60 + JSON.stringify(body, (key, value) => 61 + typeof value === 'bigint' ? value.toString() : value, 62 + ), 63 + }; 64 + 65 + export const urlSearchParamsBodySerializer = { 66 + bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>( 67 + body: T, 68 + ) => { 69 + const data = new URLSearchParams(); 70 + 71 + Object.entries(body).forEach(([key, value]) => { 72 + if (value === undefined || value === null) { 73 + return; 74 + } 75 + if (Array.isArray(value)) { 76 + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); 77 + } else { 78 + serializeUrlSearchParamsPair(data, key, value); 79 + } 80 + }); 81 + 82 + return data.toString(); 83 + }, 84 + };
+141
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/core/params.ts.snap
··· 1 + type Slot = 'body' | 'headers' | 'path' | 'query'; 2 + 3 + export type Field = 4 + | { 5 + in: Exclude<Slot, 'body'>; 6 + key: string; 7 + map?: string; 8 + } 9 + | { 10 + in: Extract<Slot, 'body'>; 11 + key?: string; 12 + map?: string; 13 + }; 14 + 15 + export interface Fields { 16 + allowExtra?: Partial<Record<Slot, boolean>>; 17 + args?: ReadonlyArray<Field>; 18 + } 19 + 20 + export type FieldsConfig = ReadonlyArray<Field | Fields>; 21 + 22 + const extraPrefixesMap: Record<string, Slot> = { 23 + $body_: 'body', 24 + $headers_: 'headers', 25 + $path_: 'path', 26 + $query_: 'query', 27 + }; 28 + const extraPrefixes = Object.entries(extraPrefixesMap); 29 + 30 + type KeyMap = Map< 31 + string, 32 + { 33 + in: Slot; 34 + map?: string; 35 + } 36 + >; 37 + 38 + const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { 39 + if (!map) { 40 + map = new Map(); 41 + } 42 + 43 + for (const config of fields) { 44 + if ('in' in config) { 45 + if (config.key) { 46 + map.set(config.key, { 47 + in: config.in, 48 + map: config.map, 49 + }); 50 + } 51 + } else if (config.args) { 52 + buildKeyMap(config.args, map); 53 + } 54 + } 55 + 56 + return map; 57 + }; 58 + 59 + interface Params { 60 + body: unknown; 61 + headers: Record<string, unknown>; 62 + path: Record<string, unknown>; 63 + query: Record<string, unknown>; 64 + } 65 + 66 + const stripEmptySlots = (params: Params) => { 67 + for (const [slot, value] of Object.entries(params)) { 68 + if (value && typeof value === 'object' && !Object.keys(value).length) { 69 + delete params[slot as Slot]; 70 + } 71 + } 72 + }; 73 + 74 + export const buildClientParams = ( 75 + args: ReadonlyArray<unknown>, 76 + fields: FieldsConfig, 77 + ) => { 78 + const params: Params = { 79 + body: {}, 80 + headers: {}, 81 + path: {}, 82 + query: {}, 83 + }; 84 + 85 + const map = buildKeyMap(fields); 86 + 87 + let config: FieldsConfig[number] | undefined; 88 + 89 + for (const [index, arg] of args.entries()) { 90 + if (fields[index]) { 91 + config = fields[index]; 92 + } 93 + 94 + if (!config) { 95 + continue; 96 + } 97 + 98 + if ('in' in config) { 99 + if (config.key) { 100 + const field = map.get(config.key)!; 101 + const name = field.map || config.key; 102 + (params[field.in] as Record<string, unknown>)[name] = arg; 103 + } else { 104 + params.body = arg; 105 + } 106 + } else { 107 + for (const [key, value] of Object.entries(arg ?? {})) { 108 + const field = map.get(key); 109 + 110 + if (field) { 111 + const name = field.map || key; 112 + (params[field.in] as Record<string, unknown>)[name] = value; 113 + } else { 114 + const extra = extraPrefixes.find(([prefix]) => 115 + key.startsWith(prefix), 116 + ); 117 + 118 + if (extra) { 119 + const [prefix, slot] = extra; 120 + (params[slot] as Record<string, unknown>)[ 121 + key.slice(prefix.length) 122 + ] = value; 123 + } else { 124 + for (const [slot, allowed] of Object.entries( 125 + config.allowExtra ?? {}, 126 + )) { 127 + if (allowed) { 128 + (params[slot as Slot] as Record<string, unknown>)[key] = value; 129 + break; 130 + } 131 + } 132 + } 133 + } 134 + } 135 + } 136 + } 137 + 138 + stripEmptySlots(params); 139 + 140 + return params; 141 + };
+179
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/core/pathSerializer.ts.snap
··· 1 + interface SerializeOptions<T> 2 + extends SerializePrimitiveOptions, 3 + SerializerOptions<T> {} 4 + 5 + interface SerializePrimitiveOptions { 6 + allowReserved?: boolean; 7 + name: string; 8 + } 9 + 10 + export interface SerializerOptions<T> { 11 + /** 12 + * @default true 13 + */ 14 + explode: boolean; 15 + style: T; 16 + } 17 + 18 + export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; 19 + export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; 20 + type MatrixStyle = 'label' | 'matrix' | 'simple'; 21 + export type ObjectStyle = 'form' | 'deepObject'; 22 + type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; 23 + 24 + interface SerializePrimitiveParam extends SerializePrimitiveOptions { 25 + value: string; 26 + } 27 + 28 + export const separatorArrayExplode = (style: ArraySeparatorStyle) => { 29 + switch (style) { 30 + case 'label': 31 + return '.'; 32 + case 'matrix': 33 + return ';'; 34 + case 'simple': 35 + return ','; 36 + default: 37 + return '&'; 38 + } 39 + }; 40 + 41 + export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { 42 + switch (style) { 43 + case 'form': 44 + return ','; 45 + case 'pipeDelimited': 46 + return '|'; 47 + case 'spaceDelimited': 48 + return '%20'; 49 + default: 50 + return ','; 51 + } 52 + }; 53 + 54 + export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { 55 + switch (style) { 56 + case 'label': 57 + return '.'; 58 + case 'matrix': 59 + return ';'; 60 + case 'simple': 61 + return ','; 62 + default: 63 + return '&'; 64 + } 65 + }; 66 + 67 + export const serializeArrayParam = ({ 68 + allowReserved, 69 + explode, 70 + name, 71 + style, 72 + value, 73 + }: SerializeOptions<ArraySeparatorStyle> & { 74 + value: unknown[]; 75 + }) => { 76 + if (!explode) { 77 + const joinedValues = ( 78 + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) 79 + ).join(separatorArrayNoExplode(style)); 80 + switch (style) { 81 + case 'label': 82 + return `.${joinedValues}`; 83 + case 'matrix': 84 + return `;${name}=${joinedValues}`; 85 + case 'simple': 86 + return joinedValues; 87 + default: 88 + return `${name}=${joinedValues}`; 89 + } 90 + } 91 + 92 + const separator = separatorArrayExplode(style); 93 + const joinedValues = value 94 + .map((v) => { 95 + if (style === 'label' || style === 'simple') { 96 + return allowReserved ? v : encodeURIComponent(v as string); 97 + } 98 + 99 + return serializePrimitiveParam({ 100 + allowReserved, 101 + name, 102 + value: v as string, 103 + }); 104 + }) 105 + .join(separator); 106 + return style === 'label' || style === 'matrix' 107 + ? separator + joinedValues 108 + : joinedValues; 109 + }; 110 + 111 + export const serializePrimitiveParam = ({ 112 + allowReserved, 113 + name, 114 + value, 115 + }: SerializePrimitiveParam) => { 116 + if (value === undefined || value === null) { 117 + return ''; 118 + } 119 + 120 + if (typeof value === 'object') { 121 + throw new Error( 122 + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', 123 + ); 124 + } 125 + 126 + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; 127 + }; 128 + 129 + export const serializeObjectParam = ({ 130 + allowReserved, 131 + explode, 132 + name, 133 + style, 134 + value, 135 + valueOnly, 136 + }: SerializeOptions<ObjectSeparatorStyle> & { 137 + value: Record<string, unknown> | Date; 138 + valueOnly?: boolean; 139 + }) => { 140 + if (value instanceof Date) { 141 + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; 142 + } 143 + 144 + if (style !== 'deepObject' && !explode) { 145 + let values: string[] = []; 146 + Object.entries(value).forEach(([key, v]) => { 147 + values = [ 148 + ...values, 149 + key, 150 + allowReserved ? (v as string) : encodeURIComponent(v as string), 151 + ]; 152 + }); 153 + const joinedValues = values.join(','); 154 + switch (style) { 155 + case 'form': 156 + return `${name}=${joinedValues}`; 157 + case 'label': 158 + return `.${joinedValues}`; 159 + case 'matrix': 160 + return `;${name}=${joinedValues}`; 161 + default: 162 + return joinedValues; 163 + } 164 + } 165 + 166 + const separator = separatorObjectExplode(style); 167 + const joinedValues = Object.entries(value) 168 + .map(([key, v]) => 169 + serializePrimitiveParam({ 170 + allowReserved, 171 + name: style === 'deepObject' ? `${name}[${key}]` : key, 172 + value: v as string, 173 + }), 174 + ) 175 + .join(separator); 176 + return style === 'label' || style === 'matrix' 177 + ? separator + joinedValues 178 + : joinedValues; 179 + };
+98
packages/openapi-ts-tests/test/__snapshots__/test/generated/v3_no_index/core/types.ts.snap
··· 1 + import type { Auth, AuthToken } from './auth'; 2 + import type { 3 + BodySerializer, 4 + QuerySerializer, 5 + QuerySerializerOptions, 6 + } from './bodySerializer'; 7 + 8 + export interface Client< 9 + RequestFn = never, 10 + Config = unknown, 11 + MethodFn = never, 12 + BuildUrlFn = never, 13 + > { 14 + /** 15 + * Returns the final request URL. 16 + */ 17 + buildUrl: BuildUrlFn; 18 + connect: MethodFn; 19 + delete: MethodFn; 20 + get: MethodFn; 21 + getConfig: () => Config; 22 + head: MethodFn; 23 + options: MethodFn; 24 + patch: MethodFn; 25 + post: MethodFn; 26 + put: MethodFn; 27 + request: RequestFn; 28 + setConfig: (config: Config) => Config; 29 + trace: MethodFn; 30 + } 31 + 32 + export interface Config { 33 + /** 34 + * Auth token or a function returning auth token. The resolved value will be 35 + * added to the request payload as defined by its `security` array. 36 + */ 37 + auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken; 38 + /** 39 + * A function for serializing request body parameter. By default, 40 + * {@link JSON.stringify()} will be used. 41 + */ 42 + bodySerializer?: BodySerializer | null; 43 + /** 44 + * An object containing any HTTP headers that you want to pre-populate your 45 + * `Headers` object with. 46 + * 47 + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} 48 + */ 49 + headers?: 50 + | RequestInit['headers'] 51 + | Record< 52 + string, 53 + | string 54 + | number 55 + | boolean 56 + | (string | number | boolean)[] 57 + | null 58 + | undefined 59 + | unknown 60 + >; 61 + /** 62 + * The request method. 63 + * 64 + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} 65 + */ 66 + method?: 67 + | 'CONNECT' 68 + | 'DELETE' 69 + | 'GET' 70 + | 'HEAD' 71 + | 'OPTIONS' 72 + | 'PATCH' 73 + | 'POST' 74 + | 'PUT' 75 + | 'TRACE'; 76 + /** 77 + * A function for serializing request query parameters. By default, arrays 78 + * will be exploded in form style, objects will be exploded in deepObject 79 + * style, and reserved characters are percent-encoded. 80 + * 81 + * This method will have no effect if the native `paramsSerializer()` Axios 82 + * API function is used. 83 + * 84 + * {@link https://swagger.io/docs/specification/serialization/#query View examples} 85 + */ 86 + querySerializer?: QuerySerializer | QuerySerializerOptions; 87 + /** 88 + * A function transforming response data before it's returned. This is useful 89 + * for post-processing data, e.g. converting ISO strings into Date objects. 90 + */ 91 + responseTransformer?: (data: unknown) => Promise<unknown>; 92 + /** 93 + * A function validating response data. This is useful if you want to ensure 94 + * the response conforms to the desired shape, so it can be safely passed to 95 + * the transformers and returned to the user. 96 + */ 97 + responseValidator?: (data: unknown) => Promise<unknown>; 98 + }
+7 -2
packages/openapi-ts-tests/test/openapi-ts.config.ts
··· 1 1 /* eslint-disable @typescript-eslint/no-unused-vars */ 2 2 import path from 'node:path'; 3 3 4 - // @ts-ignor 4 + // @ts-ignore 5 5 import { customClientPlugin } from '@hey-api/custom-client/plugin'; 6 6 import { defineConfig } from '@hey-api/openapi-ts'; 7 7 ··· 86 86 // indexFile: false, 87 87 // lint: 'eslint', 88 88 path: path.resolve(__dirname, 'generated', 'sample'), 89 + tsConfigPath: path.resolve( 90 + __dirname, 91 + 'tsconfig', 92 + 'tsconfig.nodenext.json', 93 + ), 89 94 }, 90 95 plugins: [ 91 - customClientPlugin(), 96 + // customClientPlugin(), 92 97 // myClientPlugin(), 93 98 { 94 99 // baseUrl: false,
+42
packages/openapi-ts/src/generate/client.ts
··· 2 2 import path from 'node:path'; 3 3 import { fileURLToPath } from 'node:url'; 4 4 5 + import ts from 'typescript'; 6 + 5 7 import type { ImportExportItemObject } from '../compiler/utils'; 6 8 import type { Client } from '../plugins/@hey-api/client-core/types'; 7 9 import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; ··· 61 63 }, 62 64 } satisfies Record<string, ImportExportItemObject>; 63 65 66 + const replaceRelativeImports = (filePath: string) => { 67 + let content = fs.readFileSync(filePath, 'utf8'); 68 + 69 + // Replace relative imports to append .js extension for ESM compatibility 70 + // This handles patterns like: from './foo' -> from './foo.js' 71 + // and: from '../bar' -> from '../bar.js' 72 + content = content.replace( 73 + /from\s+['"](\.\.?\/[^'"]*?)['"]/g, 74 + (match, importPath) => { 75 + // Don't add .js if it already has an extension 76 + const lastSlashIndex = importPath.lastIndexOf('/'); 77 + const fileName = 78 + lastSlashIndex >= 0 ? importPath.slice(lastSlashIndex + 1) : importPath; 79 + if (fileName.includes('.')) { 80 + return match; 81 + } 82 + return `from '${importPath}.js'`; 83 + }, 84 + ); 85 + 86 + fs.writeFileSync(filePath, content, 'utf8'); 87 + }; 88 + 64 89 /** 65 90 * Creates a `client` folder containing the same modules as the client package. 66 91 */ 67 92 export const generateClientBundle = ({ 68 93 outputPath, 69 94 plugin, 95 + tsConfig, 70 96 }: { 71 97 outputPath: string; 72 98 plugin: Plugin.Config<Client.Config & { name: any }>; 99 + tsConfig: ts.ParsedCommandLine | null; 73 100 }): void => { 74 101 // copy Hey API clients to output 75 102 const isHeyApiClientPlugin = plugin.name.startsWith('@hey-api/client-'); 76 103 if (isHeyApiClientPlugin) { 104 + const shouldAppendJs = 105 + tsConfig?.options.moduleResolution === ts.ModuleResolutionKind.NodeNext; 106 + 77 107 // copy client core 78 108 const coreOutputPath = path.resolve(outputPath, 'core'); 79 109 ensureDirSync(coreOutputPath); 80 110 const coreDistPath = path.resolve(__dirname, 'clients', 'core'); 81 111 fs.cpSync(coreDistPath, coreOutputPath, { recursive: true }); 112 + if (shouldAppendJs) { 113 + const coreFiles = fs.readdirSync(coreOutputPath); 114 + for (const file of coreFiles) { 115 + replaceRelativeImports(path.resolve(coreOutputPath, file)); 116 + } 117 + } 82 118 // copy client bundle 83 119 const clientOutputPath = path.resolve(outputPath, 'client'); 84 120 ensureDirSync(clientOutputPath); ··· 89 125 clientDistFolderName, 90 126 ); 91 127 fs.cpSync(clientDistPath, clientOutputPath, { recursive: true }); 128 + if (shouldAppendJs) { 129 + const clientFiles = fs.readdirSync(clientOutputPath); 130 + for (const file of clientFiles) { 131 + replaceRelativeImports(path.resolve(clientOutputPath, file)); 132 + } 133 + } 92 134 return; 93 135 } 94 136
+5 -1
packages/openapi-ts/src/generate/files.ts
··· 319 319 shouldAppendJs && 320 320 (resolvedModule.startsWith('./') || resolvedModule.startsWith('../')) 321 321 ) { 322 - resolvedModule = `${resolvedModule}.js`; 322 + if (resolvedModule === './client') { 323 + resolvedModule = './client/index.js'; 324 + } else { 325 + resolvedModule = `${resolvedModule}.js`; 326 + } 323 327 } 324 328 325 329 const node = compiler.namedImportDeclarations({
+3 -2
packages/openapi-ts/src/generate/legacy/output.ts
··· 57 57 removeDirSync(outputPath); 58 58 } 59 59 60 + const tsConfig = loadTsConfig(findTsConfigPath(config.output.tsConfigPath)); 61 + 60 62 const clientPlugin = getClientPlugin(config); 61 63 if ( 62 64 !isLegacyClient(config) && ··· 66 68 generateClientBundle({ 67 69 outputPath, 68 70 plugin: clientPlugin, 71 + tsConfig, 69 72 }); 70 73 } 71 74 ··· 101 104 102 105 // TODO: exports do not support .js extensions 103 106 generateIndexFile({ files }); 104 - 105 - const tsConfig = loadTsConfig(findTsConfigPath(config.output.tsConfigPath)); 106 107 107 108 Object.entries(files).forEach(([name, file]) => { 108 109 if (config.dryRun) {
+12 -7
packages/openapi-ts/src/generate/output.ts
··· 18 18 removeDirSync(outputPath); 19 19 } 20 20 21 + const tsConfig = loadTsConfig( 22 + findTsConfigPath(context.config.output.tsConfigPath), 23 + ); 24 + const shouldAppendJs = 25 + tsConfig?.options.moduleResolution === ts.ModuleResolutionKind.NodeNext; 26 + 21 27 const client = getClientPlugin(context.config); 22 28 if ('bundle' in client && client.bundle) { 23 29 generateClientBundle({ 24 30 outputPath, 25 31 plugin: client, 32 + tsConfig, 26 33 }); 27 34 } 28 35 ··· 50 57 path: 'index', 51 58 }); 52 59 53 - const tsConfig = loadTsConfig( 54 - findTsConfigPath(context.config.output.tsConfigPath), 55 - ); 56 - const shouldAppendJs = 57 - tsConfig?.options.moduleResolution === ts.ModuleResolutionKind.NodeNext; 58 - 59 60 for (const file of Object.values(context.files)) { 60 61 const fileName = file.nameWithoutExtension(); 61 62 ··· 76 77 shouldAppendJs && 77 78 (resolvedModule.startsWith('./') || resolvedModule.startsWith('../')) 78 79 ) { 79 - resolvedModule = `${resolvedModule}.js`; 80 + if (resolvedModule === './client') { 81 + resolvedModule = './client/index.js'; 82 + } else { 83 + resolvedModule = `${resolvedModule}.js`; 84 + } 80 85 } 81 86 // TODO: parser - add export method for more granular control over 82 87 // what's exported so we can support named exports
+29 -1
packages/openapi-ts/src/plugins/@hey-api/client-nuxt/__tests__/client.test.ts
··· 1 - import { describe, expect, it } from 'vitest'; 1 + import { describe, expect, it, vi } from 'vitest'; 2 + 3 + // Mock Nuxt composables with realistic return values 4 + vi.mock('nuxt/app', () => ({ 5 + useAsyncData: vi.fn(() => ({ 6 + data: { value: null }, 7 + error: { value: null }, 8 + pending: { value: false }, 9 + refresh: vi.fn(), 10 + })), 11 + useFetch: vi.fn(() => ({ 12 + data: { value: null }, 13 + error: { value: null }, 14 + pending: { value: false }, 15 + refresh: vi.fn(), 16 + })), 17 + useLazyAsyncData: vi.fn(() => ({ 18 + data: { value: null }, 19 + error: { value: null }, 20 + pending: { value: false }, 21 + refresh: vi.fn(), 22 + })), 23 + useLazyFetch: vi.fn(() => ({ 24 + data: { value: null }, 25 + error: { value: null }, 26 + pending: { value: false }, 27 + refresh: vi.fn(), 28 + })), 29 + })); 2 30 3 31 import { createClient } from '../bundle/client'; 4 32