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

chore: add auth

Lubos ca013b35 4216670c

+336 -578
+18
examples/openapi-ts-nuxt/app.vue
··· 1 + <script setup lang="ts"> 2 + import { client } from './client'; 3 + 4 + // configure internal service client 5 + client.setConfig({ 6 + auth: () => { 7 + // fetch auth token 8 + return undefined; 9 + }, 10 + // set default base url for requests 11 + baseURL: 'https://petstore3.swagger.io/api/v3', 12 + // set default headers for requests 13 + headers: { 14 + Authorization: 'Bearer <token_from_service_client>', 15 + }, 16 + }); 17 + </script> 18 + 1 19 <template> 2 20 <div> 3 21 <NuxtRouteAnnouncer />
+43 -47
examples/openapi-ts-nuxt/client/sdk.gen.ts
··· 1 - // @ts-nocheck 2 1 // This file is auto-generated by @hey-api/openapi-ts 3 2 4 3 import { ··· 50 49 * Add a new pet to the store 51 50 */ 52 51 export const addPet = <TComposable extends Composable>( 53 - options: Options<AddPetData, unknown, TComposable>, 52 + options: Options<TComposable, AddPetData>, 54 53 ) => 55 - (options?.client ?? client).post<AddPetResponse, unknown, TComposable>({ 54 + (options?.client ?? client).post<TComposable, AddPetResponse, unknown>({ 56 55 ...options, 57 56 headers: { 58 57 'Content-Type': 'application/json', ··· 66 65 * Update an existing pet by Id 67 66 */ 68 67 export const updatePet = <TComposable extends Composable>( 69 - options: Options<UpdatePetData, unknown, TComposable>, 68 + options: Options<TComposable, UpdatePetData>, 70 69 ) => 71 - (options?.client ?? client).put<UpdatePetResponse, unknown, TComposable>({ 70 + (options?.client ?? client).put<TComposable, UpdatePetResponse, unknown>({ 72 71 ...options, 73 72 headers: { 74 73 'Content-Type': 'application/json', ··· 82 81 * Multiple status values can be provided with comma separated strings 83 82 */ 84 83 export const findPetsByStatus = <TComposable extends Composable>( 85 - options?: Options<FindPetsByStatusData, unknown, TComposable>, 84 + options: Options<TComposable, FindPetsByStatusData>, 86 85 ) => 87 86 (options?.client ?? client).get< 87 + TComposable, 88 88 FindPetsByStatusResponse, 89 - unknown, 90 - TComposable 89 + unknown 91 90 >({ 92 91 ...options, 93 92 url: '/pet/findByStatus', ··· 98 97 * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. 99 98 */ 100 99 export const findPetsByTags = <TComposable extends Composable>( 101 - options?: Options<FindPetsByTagsData, unknown, TComposable>, 100 + options: Options<TComposable, FindPetsByTagsData>, 102 101 ) => 103 - (options?.client ?? client).get<FindPetsByTagsResponse, unknown, TComposable>( 102 + (options?.client ?? client).get<TComposable, FindPetsByTagsResponse, unknown>( 104 103 { 105 104 ...options, 106 105 url: '/pet/findByTags', ··· 111 110 * Deletes a pet 112 111 */ 113 112 export const deletePet = <TComposable extends Composable>( 114 - options: Options<DeletePetData, unknown, TComposable>, 113 + options: Options<TComposable, DeletePetData>, 115 114 ) => 116 - (options?.client ?? client).delete<unknown, unknown, TComposable>({ 115 + (options?.client ?? client).delete<TComposable, unknown, unknown>({ 117 116 ...options, 118 117 url: '/pet/{petId}', 119 118 }); ··· 123 122 * Returns a single pet 124 123 */ 125 124 export const getPetById = <TComposable extends Composable>( 126 - options: Options<GetPetByIdData, unknown, TComposable>, 125 + options: Options<TComposable, GetPetByIdData>, 127 126 ) => 128 - (options?.client ?? client).get<GetPetByIdResponse, unknown, TComposable>({ 127 + (options?.client ?? client).get<TComposable, GetPetByIdResponse, unknown>({ 129 128 ...options, 130 129 security: [ 131 130 { 132 - fn: 'apiKey', 133 - in: 'header', 134 131 name: 'api_key', 132 + type: 'apiKey', 135 133 }, 136 134 ], 137 - // url: '/pet/{petId}', 138 - url: '/pet/8', 135 + url: '/pet/{petId}', 139 136 }); 140 137 141 138 /** 142 139 * Updates a pet in the store with form data 143 140 */ 144 141 export const updatePetWithForm = <TComposable extends Composable>( 145 - options: Options<UpdatePetWithFormData, unknown, TComposable>, 142 + options: Options<TComposable, UpdatePetWithFormData>, 146 143 ) => 147 - (options?.client ?? client).post<unknown, unknown, TComposable>({ 144 + (options?.client ?? client).post<TComposable, unknown, unknown>({ 148 145 ...options, 149 146 url: '/pet/{petId}', 150 147 }); ··· 153 150 * uploads an image 154 151 */ 155 152 export const uploadFile = <TComposable extends Composable>( 156 - options: Options<UploadFileData, unknown, TComposable>, 153 + options: Options<TComposable, UploadFileData>, 157 154 ) => 158 - (options?.client ?? client).post<UploadFileResponse, unknown, TComposable>({ 155 + (options?.client ?? client).post<TComposable, UploadFileResponse, unknown>({ 159 156 ...options, 160 157 headers: { 161 158 'Content-Type': 'application/octet-stream', ··· 169 166 * Returns a map of status codes to quantities 170 167 */ 171 168 export const getInventory = <TComposable extends Composable>( 172 - options?: Options<GetInventoryData, unknown, TComposable>, 169 + options: Options<TComposable, GetInventoryData>, 173 170 ) => 174 - (options?.client ?? client).get<GetInventoryResponse, unknown, TComposable>({ 171 + (options?.client ?? client).get<TComposable, GetInventoryResponse, unknown>({ 175 172 ...options, 176 173 security: [ 177 174 { 178 - fn: 'apiKey', 179 - in: 'header', 180 175 name: 'api_key', 176 + type: 'apiKey', 181 177 }, 182 178 ], 183 179 url: '/store/inventory', ··· 188 184 * Place a new order in the store 189 185 */ 190 186 export const placeOrder = <TComposable extends Composable>( 191 - options?: Options<PlaceOrderData, unknown, TComposable>, 187 + options: Options<TComposable, PlaceOrderData>, 192 188 ) => 193 - (options?.client ?? client).post<PlaceOrderResponse, unknown, TComposable>({ 189 + (options?.client ?? client).post<TComposable, PlaceOrderResponse, unknown>({ 194 190 ...options, 195 191 headers: { 196 192 'Content-Type': 'application/json', ··· 204 200 * For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors 205 201 */ 206 202 export const deleteOrder = <TComposable extends Composable>( 207 - options: Options<DeleteOrderData, unknown, TComposable>, 203 + options: Options<TComposable, DeleteOrderData>, 208 204 ) => 209 - (options?.client ?? client).delete<unknown, unknown, TComposable>({ 205 + (options?.client ?? client).delete<TComposable, unknown, unknown>({ 210 206 ...options, 211 207 url: '/store/order/{orderId}', 212 208 }); ··· 216 212 * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions. 217 213 */ 218 214 export const getOrderById = <TComposable extends Composable>( 219 - options: Options<GetOrderByIdData, unknown, TComposable>, 215 + options: Options<TComposable, GetOrderByIdData>, 220 216 ) => 221 - (options?.client ?? client).get<GetOrderByIdResponse, unknown, TComposable>({ 217 + (options?.client ?? client).get<TComposable, GetOrderByIdResponse, unknown>({ 222 218 ...options, 223 219 url: '/store/order/{orderId}', 224 220 }); ··· 228 224 * This can only be done by the logged in user. 229 225 */ 230 226 export const createUser = <TComposable extends Composable>( 231 - options?: Options<CreateUserData, unknown, TComposable>, 227 + options: Options<TComposable, CreateUserData>, 232 228 ) => 233 - (options?.client ?? client).post<CreateUserResponse, unknown, TComposable>({ 229 + (options?.client ?? client).post<TComposable, CreateUserResponse, unknown>({ 234 230 ...options, 235 231 headers: { 236 232 'Content-Type': 'application/json', ··· 244 240 * Creates list of users with given input array 245 241 */ 246 242 export const createUsersWithListInput = <TComposable extends Composable>( 247 - options?: Options<CreateUsersWithListInputData, unknown, TComposable>, 243 + options: Options<TComposable, CreateUsersWithListInputData>, 248 244 ) => 249 245 (options?.client ?? client).post< 246 + TComposable, 250 247 CreateUsersWithListInputResponse, 251 - unknown, 252 - TComposable 248 + unknown 253 249 >({ 254 250 ...options, 255 251 headers: { ··· 263 259 * Logs user into the system 264 260 */ 265 261 export const loginUser = <TComposable extends Composable>( 266 - options?: Options<LoginUserData, unknown, TComposable>, 262 + options: Options<TComposable, LoginUserData>, 267 263 ) => 268 - (options?.client ?? client).get<LoginUserResponse, unknown, TComposable>({ 264 + (options?.client ?? client).get<TComposable, LoginUserResponse, unknown>({ 269 265 ...options, 270 266 url: '/user/login', 271 267 }); ··· 274 270 * Logs out current logged in user session 275 271 */ 276 272 export const logoutUser = <TComposable extends Composable>( 277 - options?: Options<LogoutUserData, unknown, TComposable>, 273 + options: Options<TComposable, LogoutUserData>, 278 274 ) => 279 - (options?.client ?? client).get<unknown, unknown, TComposable>({ 275 + (options?.client ?? client).get<TComposable, unknown, unknown>({ 280 276 ...options, 281 277 url: '/user/logout', 282 278 }); ··· 286 282 * This can only be done by the logged in user. 287 283 */ 288 284 export const deleteUser = <TComposable extends Composable>( 289 - options: Options<DeleteUserData, unknown, TComposable>, 285 + options: Options<TComposable, DeleteUserData>, 290 286 ) => 291 - (options?.client ?? client).delete<unknown, unknown, TComposable>({ 287 + (options?.client ?? client).delete<TComposable, unknown, unknown>({ 292 288 ...options, 293 289 url: '/user/{username}', 294 290 }); ··· 297 293 * Get user by user name 298 294 */ 299 295 export const getUserByName = <TComposable extends Composable>( 300 - options: Options<GetUserByNameData, unknown, TComposable>, 296 + options: Options<TComposable, GetUserByNameData>, 301 297 ) => 302 - (options?.client ?? client).get<GetUserByNameResponse, unknown, TComposable>({ 298 + (options?.client ?? client).get<TComposable, GetUserByNameResponse, unknown>({ 303 299 ...options, 304 300 url: '/user/{username}', 305 301 }); ··· 309 305 * This can only be done by the logged in user. 310 306 */ 311 307 export const updateUser = <TComposable extends Composable>( 312 - options: Options<UpdateUserData, unknown, TComposable>, 308 + options: Options<TComposable, UpdateUserData>, 313 309 ) => 314 - (options?.client ?? client).put<unknown, unknown, TComposable>({ 310 + (options?.client ?? client).put<TComposable, unknown, unknown>({ 315 311 ...options, 316 312 headers: { 317 313 'Content-Type': 'application/json',
+68 -57
examples/openapi-ts-nuxt/components/home.vue
··· 1 1 <script setup lang="ts"> 2 - import { getPetById, type Pet } from '~/client'; 3 - 4 - // START 5 - const baseUrl = 'https://petstore3.swagger.io/api/v3'; 6 - const finalUrl = `${baseUrl}/pet/8`; 2 + import { getPetById } from '~/client'; 7 3 8 - // $fetch 9 - // During SSR data is fetched twice, once on the server and once on the client. 10 - const dollarFetch = await getPetById({ 11 - composable: '$fetch', 4 + /** 5 + * useAsyncData 6 + * 7 + * During SSR data is fetched only on the server side and transferred to the 8 + * client. 9 + * 10 + * This will NOT forward anything. 11 + * Result: { cookies: {} } 12 + */ 13 + const asyncData = await getPetById({ 14 + asyncDataOptions: {}, 15 + composable: 'useAsyncData', 16 + key: 'item', 17 + path: { 18 + petId: 8, 19 + }, 12 20 }); 13 - const dollarFetchNuxt = await $fetch<Pet>(finalUrl); 14 21 15 - // useAsyncData 16 - // During SSR data is fetched only on the server side and transferred to the client. 17 - const asyncData = await getPetById({ 22 + /** 23 + * useAsyncData + useRequestFetch 24 + * 25 + * This will forward the user's headers to the event handler. 26 + * Result: { cookies: { foo: 'bar' } } 27 + */ 28 + const requestFetch = useRequestFetch(); 29 + const asyncDataWithRequestFetch = await getPetById({ 30 + $fetch: requestFetch, 18 31 composable: 'useAsyncData', 19 - key: 'item', 20 - asyncDataOptions: {}, 32 + path: { 33 + petId: 8, 34 + }, 21 35 }); 22 - const asyncDataNuxt = await useAsyncData<Pet>(() => $fetch(finalUrl)); 23 - const asyncDataWithKeyNuxt = await useAsyncData<Pet>('item', () => 24 - $fetch(finalUrl), 25 - ); 26 36 27 - // useFetch 28 - // You can also useFetch as shortcut of useAsyncData + $fetch 37 + /** 38 + * useFetch 39 + * 40 + * You can also useFetch as shortcut of useAsyncData + $fetch. 41 + */ 29 42 const fetch = await getPetById({ 30 43 composable: 'useFetch', 31 - fetchOptions: {}, 44 + path: { 45 + petId: 8, 46 + }, 32 47 }); 33 - const fetchNuxt = await useFetch<Pet>(finalUrl); 34 48 35 - // useLazyAsyncData 36 - /* Navigation will occur before fetching is complete. 37 - Handle 'pending' and 'error' states directly within your component's template 38 - */ 49 + /** 50 + * useLazyAsyncData 51 + * 52 + * Navigation will occur before fetching is complete. Handle 'pending' and 53 + * 'error' states directly within your component's template. 54 + */ 39 55 const lazyAsyncData = await getPetById({ 40 56 composable: 'useLazyAsyncData', 41 57 key: 'count', 58 + path: { 59 + petId: 8, 60 + }, 42 61 }); 43 - const lazyAsyncDataNuxt = await useLazyAsyncData<Pet>('count', () => 44 - $fetch(finalUrl), 45 - ); 46 - watch(lazyAsyncDataNuxt.data, (newCount) => { 47 - // Because count might start out null, you won't have access 62 + watch(lazyAsyncData.data, (newPet) => { 63 + // Because pet might start out null, you won't have access 48 64 // to its contents immediately, but you can watch it. 65 + if (newPet) { 66 + console.log(newPet.name); 67 + } 49 68 }); 50 69 51 - // useLazyFetch 52 - /* Navigation will occur before fetching is complete. 53 - * Handle 'pending' and 'error' states directly within your component's template 70 + /** 71 + * useLazyFetch 72 + * 73 + * Navigation will occur before fetching is complete. Handle 'pending' and 74 + * 'error' states directly within your component's template. 54 75 */ 55 76 const lazyFetch = await getPetById({ 56 77 composable: 'useLazyFetch', 78 + path: { 79 + petId: 8, 80 + }, 57 81 }); 58 - const lazyFetchNuxt = await useLazyFetch<Pet>(finalUrl); 59 - watch(lazyFetchNuxt.data, (newPosts) => { 60 - // Because posts might start out null, you won't have access 82 + watch(lazyFetch.data, (newPet) => { 83 + // Because pet might start out null, you won't have access 61 84 // to its contents immediately, but you can watch it. 85 + if (newPet) { 86 + console.log(newPet.name); 87 + } 62 88 }); 63 89 64 - // useRequestFetch 65 - // This will forward the user's headers to the `/api/foo` event handler 66 - // Result: { cookies: { foo: 'bar' } } 67 - const requestFetch = useRequestFetch(); 68 - const asyncDataFinal = await getPetById({ 69 - composable: 'useAsyncData', 70 - requestFetch, 71 - }); 72 - const asyncData2 = await useAsyncData<Pet>(() => requestFetch(finalUrl)); 73 - // This will NOT forward anything 74 - // Result: { cookies: {} } 75 - const asyncData3 = await useAsyncData<Pet>(() => $fetch(finalUrl)); 76 - 77 - // END 78 - 79 - async function handleClick() { 90 + async function handleFetch() { 80 91 const result = await getPetById({ 81 92 composable: '$fetch', 82 - // @ts-expect-error 83 93 path: { 84 94 petId: 8, 85 95 }, 86 96 }); 87 - console.warn(result); 97 + console.log(result); 88 98 } 89 99 </script> 90 100 91 101 <template> 102 + <h1>Get Random Pet Nuxt APIs</h1> 92 103 <div> 93 - <button @click="handleClick">Get Random Pet</button> 104 + <button @click="handleFetch" type="button">$fetch</button> 94 105 </div> 95 106 </template>
+19 -19
packages/client-axios/src/types.ts
··· 135 135 } 136 136 137 137 export type RequestResult< 138 - Data = unknown, 138 + TData = unknown, 139 139 TError = unknown, 140 140 ThrowOnError extends boolean = boolean, 141 141 > = ThrowOnError extends true 142 - ? Promise<AxiosResponse<Data>> 142 + ? Promise<AxiosResponse<TData>> 143 143 : Promise< 144 - | (AxiosResponse<Data> & { error: undefined }) 144 + | (AxiosResponse<TData> & { error: undefined }) 145 145 | (AxiosError<TError> & { data: undefined; error: TError }) 146 146 >; 147 147 148 148 type MethodFn = < 149 - Data = unknown, 149 + TData = unknown, 150 150 TError = unknown, 151 151 ThrowOnError extends boolean = false, 152 152 >( 153 153 options: Omit<RequestOptions<ThrowOnError>, 'method'>, 154 - ) => RequestResult<Data, TError, ThrowOnError>; 154 + ) => RequestResult<TData, TError, ThrowOnError>; 155 155 156 156 type RequestFn = < 157 - Data = unknown, 157 + TData = unknown, 158 158 TError = unknown, 159 159 ThrowOnError extends boolean = false, 160 160 >( 161 161 options: Omit<RequestOptions<ThrowOnError>, 'method'> & 162 162 Pick<Required<RequestOptions<ThrowOnError>>, 'method'>, 163 - ) => RequestResult<Data, TError, ThrowOnError>; 163 + ) => RequestResult<TData, TError, ThrowOnError>; 164 164 165 165 export interface Client { 166 166 /** 167 167 * Returns the final request URL. This method works only with experimental parser. 168 168 */ 169 169 buildUrl: < 170 - Data extends { 170 + TData extends { 171 171 body?: unknown; 172 172 path?: Record<string, unknown>; 173 173 query?: Record<string, unknown>; 174 174 url: string; 175 175 }, 176 176 >( 177 - options: Pick<Data, 'url'> & Omit<Options<Data>, 'axios'>, 177 + options: Pick<TData, 'url'> & Omit<Options<TData>, 'axios'>, 178 178 ) => string; 179 179 delete: MethodFn; 180 180 get: MethodFn; ··· 198 198 } 199 199 200 200 export type Options< 201 - Data extends DataShape = DataShape, 201 + TData extends DataShape = DataShape, 202 202 ThrowOnError extends boolean = boolean, 203 203 > = OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'path' | 'query' | 'url'> & 204 - Omit<Data, 'url'>; 204 + Omit<TData, 'url'>; 205 205 206 206 export type OptionsLegacyParser< 207 - Data = unknown, 207 + TData = unknown, 208 208 ThrowOnError extends boolean = boolean, 209 - > = Data extends { body?: any } 210 - ? Data extends { headers?: any } 211 - ? OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'headers' | 'url'> & Data 209 + > = TData extends { body?: any } 210 + ? TData extends { headers?: any } 211 + ? OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'headers' | 'url'> & TData 212 212 : OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'url'> & 213 - Data & 213 + TData & 214 214 Pick<RequestOptions<ThrowOnError>, 'headers'> 215 - : Data extends { headers?: any } 215 + : TData extends { headers?: any } 216 216 ? OmitKeys<RequestOptions<ThrowOnError>, 'headers' | 'url'> & 217 - Data & 217 + TData & 218 218 Pick<RequestOptions<ThrowOnError>, 'body'> 219 - : OmitKeys<RequestOptions<ThrowOnError>, 'url'> & Data; 219 + : OmitKeys<RequestOptions<ThrowOnError>, 'url'> & TData;
+55 -155
packages/client-nuxt/src/index.ts
··· 6 6 useLazyFetch, 7 7 } from 'nuxt/app'; 8 8 9 - import type { Client, Config, RequestOptions } from './types'; 9 + import type { Client, Config } from './types'; 10 10 import { 11 11 buildUrl, 12 12 createConfig, 13 - createInterceptors, 14 - getParseAs, 15 13 mergeConfigs, 16 14 mergeHeaders, 17 15 setAuthParams, 18 16 } from './utils'; 19 17 20 - type ReqInit = Omit<RequestInit, 'body' | 'headers'> & { 21 - body?: any; 22 - headers: ReturnType<typeof mergeHeaders>; 23 - }; 24 - 25 18 export const createClient = (config: Config = {}): Client => { 26 19 let _config = mergeConfigs(createConfig(), config); 27 20 ··· 32 25 return getConfig(); 33 26 }; 34 27 35 - const interceptors = createInterceptors< 36 - Request, 37 - Response, 38 - unknown, 39 - RequestOptions 40 - >(); 41 - 42 - const clientRequest: Client['clientRequest'] = ({ 28 + const request: Client['request'] = ({ 43 29 asyncDataOptions, 44 30 composable, 45 - fetchOptions, 46 31 key, 47 - requestFetch, 48 - url, 49 - ...opts 32 + ...options 50 33 }) => { 51 - const fetchFn = requestFetch ?? $fetch; 52 - const baseUrl = 'https://petstore3.swagger.io/api/v3'; 53 - const finalUrl = `${baseUrl}${url}`; 54 - 55 - if (composable === '$fetch') { 56 - return fetchFn(finalUrl, opts); 57 - } 58 - 59 - if (composable === 'useFetch') { 60 - return useFetch(finalUrl, { 61 - ...opts, 62 - ...fetchOptions, 63 - }); 64 - } 65 - 66 - if (composable === 'useLazyFetch') { 67 - return useLazyFetch(finalUrl, { 68 - ...opts, 69 - ...fetchOptions, 70 - }); 71 - } 72 - 73 - const handler: (ctx?: NuxtApp) => Promise<any> = () => 74 - fetchFn(finalUrl, opts); 75 - 76 - if (composable === 'useAsyncData') { 77 - return key 78 - ? useAsyncData(key, handler, asyncDataOptions) 79 - : useAsyncData(handler, asyncDataOptions); 80 - } 81 - 82 - if (composable === 'useLazyAsyncData') { 83 - return key 84 - ? useLazyAsyncData(key, handler, asyncDataOptions) 85 - : useLazyAsyncData(handler, asyncDataOptions); 86 - } 87 - 88 - return undefined as any; 89 - }; 90 - 91 - // @ts-expect-error 92 - const request: Client['request'] = async (options) => { 93 34 const opts = { 94 35 ..._config, 95 36 ...options, 96 - fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, 37 + $fetch: options.$fetch ?? _config.$fetch ?? $fetch, 97 38 headers: mergeHeaders(_config.headers, options.headers), 98 39 }; 99 40 100 - if (opts.security) { 101 - await setAuthParams({ 102 - ...opts, 103 - security: opts.security, 104 - }); 41 + const { security } = opts; 42 + if (security) { 43 + // auth must happen in interceptors otherwise we'd need to require 44 + // asyncContext enabled 45 + // https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext 46 + opts.onRequest = async ({ options }) => { 47 + await setAuthParams({ 48 + auth: opts.auth, 49 + headers: options.headers, 50 + query: options.query, 51 + security, 52 + }); 53 + }; 105 54 } 106 55 107 56 if (opts.body && opts.bodySerializer) { ··· 114 63 } 115 64 116 65 const url = buildUrl(opts); 117 - const requestInit: ReqInit = { 118 - redirect: 'follow', 119 - ...opts, 120 - }; 121 66 122 - let request = new Request(url, requestInit); 67 + const fetchFn = opts.$fetch; 123 68 124 - for (const fn of interceptors.request._fns) { 125 - request = await fn(request, opts); 126 - } 69 + // if (parseAs === 'json') { 70 + // if (opts.responseValidator) { 71 + // await opts.responseValidator(data); 72 + // } 127 73 128 - // fetch must be assigned here, otherwise it would throw the error: 129 - // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation 130 - const _fetch = opts.fetch!; 131 - let response = await _fetch(request); 74 + // if (opts.responseTransformer) { 75 + // data = await opts.responseTransformer(data); 76 + // } 77 + // } 132 78 133 - for (const fn of interceptors.response._fns) { 134 - response = await fn(response, request, opts); 79 + if (composable === '$fetch') { 80 + // @ts-expect-error 81 + return fetchFn(url, opts); 135 82 } 136 83 137 - const result = { 138 - request, 139 - response, 140 - }; 141 - 142 - if (response.ok) { 143 - if ( 144 - response.status === 204 || 145 - response.headers.get('Content-Length') === '0' 146 - ) { 147 - return { 148 - data: {}, 149 - ...result, 150 - }; 151 - } 152 - 153 - const parseAs = 154 - (opts.parseAs === 'auto' 155 - ? getParseAs(response.headers.get('Content-Type')) 156 - : opts.parseAs) ?? 'json'; 157 - 158 - if (parseAs === 'stream') { 159 - return { 160 - data: response.body, 161 - ...result, 162 - }; 163 - } 164 - 165 - let data = await response[parseAs](); 166 - if (parseAs === 'json') { 167 - if (opts.responseValidator) { 168 - await opts.responseValidator(data); 169 - } 170 - 171 - if (opts.responseTransformer) { 172 - data = await opts.responseTransformer(data); 173 - } 174 - } 175 - 176 - return { 177 - data, 178 - ...result, 179 - }; 84 + if (composable === 'useFetch') { 85 + return useFetch(url, opts); 180 86 } 181 87 182 - let error = await response.text(); 183 - 184 - try { 185 - error = JSON.parse(error); 186 - } catch { 187 - // noop 88 + if (composable === 'useLazyFetch') { 89 + return useLazyFetch(url, opts); 188 90 } 189 91 190 - let finalError = error; 92 + const handler: (ctx?: NuxtApp) => Promise<any> = () => 93 + // @ts-expect-error 94 + fetchFn(url, opts); 191 95 192 - for (const fn of interceptors.error._fns) { 193 - finalError = (await fn(error, response, request, opts)) as string; 96 + if (composable === 'useAsyncData') { 97 + return key 98 + ? useAsyncData(key, handler, asyncDataOptions) 99 + : useAsyncData(handler, asyncDataOptions); 194 100 } 195 101 196 - finalError = finalError || ({} as string); 197 - 198 - if (opts.throwOnError) { 199 - throw finalError; 102 + if (composable === 'useLazyAsyncData') { 103 + return key 104 + ? useLazyAsyncData(key, handler, asyncDataOptions) 105 + : useLazyAsyncData(handler, asyncDataOptions); 200 106 } 201 107 202 - return { 203 - error: finalError, 204 - ...result, 205 - }; 108 + return undefined as any; 206 109 }; 207 110 208 111 return { 209 112 buildUrl, 210 - clientRequest, 211 - connect: (options) => clientRequest({ ...options, method: 'CONNECT' }), 212 - delete: (options) => clientRequest({ ...options, method: 'DELETE' }), 213 - get: (options) => clientRequest({ ...options, method: 'GET' }), 113 + connect: (options) => request({ ...options, method: 'CONNECT' }), 114 + delete: (options) => request({ ...options, method: 'DELETE' }), 115 + get: (options) => request({ ...options, method: 'GET' }), 214 116 getConfig, 215 - head: (options) => clientRequest({ ...options, method: 'HEAD' }), 216 - interceptors, 217 - options: (options) => clientRequest({ ...options, method: 'OPTIONS' }), 218 - patch: (options) => clientRequest({ ...options, method: 'PATCH' }), 219 - post: (options) => clientRequest({ ...options, method: 'POST' }), 220 - put: (options) => clientRequest({ ...options, method: 'PUT' }), 117 + head: (options) => request({ ...options, method: 'HEAD' }), 118 + options: (options) => request({ ...options, method: 'OPTIONS' }), 119 + patch: (options) => request({ ...options, method: 'PATCH' }), 120 + post: (options) => request({ ...options, method: 'POST' }), 121 + put: (options) => request({ ...options, method: 'PUT' }), 221 122 request, 222 123 setConfig, 223 - trace: (options) => clientRequest({ ...options, method: 'TRACE' }), 124 + trace: (options) => request({ ...options, method: 'TRACE' }), 224 125 }; 225 126 }; 226 127 227 128 export type { 129 + Auth, 228 130 Client, 229 131 Composable, 230 132 Config, 231 133 Options, 232 134 OptionsLegacyParser, 233 - OptionsOld, 234 135 RequestOptions, 235 136 RequestResult, 236 - Security, 237 137 } from './types'; 238 138 export { 239 139 createConfig,
+81 -149
packages/client-nuxt/src/types.ts
··· 5 5 UseFetchOptions, 6 6 useLazyAsyncData, 7 7 useLazyFetch, 8 - useRequestFetch, 9 8 } from 'nuxt/app'; 10 9 11 10 import type { 12 11 BodySerializer, 13 - Middleware, 14 12 QuerySerializer, 15 13 QuerySerializerOptions, 16 14 } from './utils'; 17 15 18 16 type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>; 19 17 20 - export interface Config<ThrowOnError extends boolean = boolean> 21 - extends Omit<RequestInit, 'body' | 'headers' | 'method'> { 18 + export interface Config 19 + extends Omit< 20 + FetchOptions<unknown>, 21 + 'baseURL' | 'body' | 'headers' | 'method' 22 + > { 22 23 /** 23 24 * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** 24 25 * 25 - * Access token or a function returning access token. The resolved token 26 - * will be added to request headers where it's required. 26 + * Auth token or a function returning auth token. The resolved value will be 27 + * added to the request payload as defined by its `security` array. 27 28 */ 28 - accessToken?: (() => Promise<string | undefined>) | string | undefined; 29 - /** 30 - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** 31 - * 32 - * API key or a function returning API key. The resolved key will be added 33 - * to the request payload as required. 34 - */ 35 - apiKey?: (() => Promise<string | undefined>) | string | undefined; 29 + auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken; 36 30 /** 37 31 * Base URL for all requests made by this client. 38 32 * 39 33 * @default '' 40 34 */ 41 - baseUrl?: string; 35 + baseURL?: string; 42 36 /** 43 37 * A function for serializing request body parameter. By default, 44 38 * {@link JSON.stringify()} will be used. 45 39 */ 46 40 bodySerializer?: BodySerializer; 47 41 /** 48 - * Fetch API implementation. You can use this option to provide a custom 49 - * fetch instance. 50 - * 51 - * @default globalThis.fetch 52 - */ 53 - fetch?: (request: Request) => ReturnType<typeof fetch>; 54 - /** 55 42 * An object containing any HTTP headers that you want to pre-populate your 56 43 * `Headers` object with. 57 44 * ··· 85 72 | 'PUT' 86 73 | 'TRACE'; 87 74 /** 88 - * Return the response data parsed in a specified format. By default, `auto` 89 - * will infer the appropriate method from the `Content-Type` response header. 90 - * You can override this behavior with any of the {@link Body} methods. 91 - * Select `stream` if you don't want to parse response data at all. 92 - * 93 - * @default 'auto' 94 - */ 95 - parseAs?: Exclude<keyof Body, 'body' | 'bodyUsed'> | 'auto' | 'stream'; 96 - /** 97 75 * A function for serializing request query parameters. By default, arrays 98 76 * will be exploded in form style, objects will be exploded in deepObject 99 77 * style, and reserved characters are percent-encoded. ··· 114 92 * the transformers and returned to the user. 115 93 */ 116 94 responseValidator?: (data: unknown) => Promise<unknown>; 117 - /** 118 - * Throw an error instead of returning it in the response? 119 - * 120 - * @default false 121 - */ 122 - throwOnError?: ThrowOnError; 95 + } 96 + 97 + export interface Auth { 98 + in?: 'header' | 'query'; 99 + name?: string; 100 + scheme?: 'basic' | 'bearer'; 101 + type: 'apiKey' | 'http'; 123 102 } 103 + 104 + type AuthToken = string | undefined; 124 105 125 106 export interface RequestOptions< 126 - ThrowOnError extends boolean = boolean, 107 + TComposable extends Composable = Composable, 127 108 Url extends string = string, 128 - > extends Config<ThrowOnError> { 109 + > extends Config { 110 + asyncDataOptions?: AsyncDataOptions<unknown>; 129 111 /** 130 112 * Any body that you want to add to your request. 131 113 * 132 114 * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} 133 115 */ 134 - body?: 135 - | RequestInit['body'] 136 - | Record<string, unknown> 137 - | Array<Record<string, unknown>> 138 - | Array<unknown> 139 - | number; 116 + body?: BodyInit | Record<string, any> | null; 140 117 /** 141 118 * You can provide a client instance returned by `createClient()` instead of 142 119 * individual options. This might be also useful if you want to implement a 143 120 * custom client. 144 121 */ 145 122 client?: Client; 123 + composable: TComposable; 124 + key?: string; 146 125 path?: Record<string, unknown>; 147 - query?: Record<string, unknown>; 126 + query?: FetchOptions<unknown>['query']; 148 127 /** 149 128 * Security mechanism(s) to use for the request. 150 129 */ 151 - security?: ReadonlyArray<Security>; 130 + security?: ReadonlyArray<Auth>; 152 131 url: Url; 153 132 } 154 133 155 134 export type RequestResult< 156 - Data = unknown, 157 - TError = unknown, 158 - ThrowOnError extends boolean = boolean, 159 - > = ThrowOnError extends true 160 - ? Promise<{ 161 - data: Data; 162 - request: Request; 163 - response: Response; 164 - }> 165 - : Promise< 166 - ( 167 - | { data: Data; error: undefined } 168 - | { data: undefined; error: TError } 169 - ) & { 170 - request: Request; 171 - response: Response; 172 - } 173 - >; 174 - 175 - export interface Security { 176 - fn: 'accessToken' | 'apiKey'; 177 - in: 'header' | 'query'; 178 - name: string; 179 - } 135 + TComposable extends Composable, 136 + TData, 137 + TError, 138 + > = TComposable extends '$fetch' 139 + ? ReturnType<typeof $fetch<TData>> 140 + : TComposable extends 'useAsyncData' 141 + ? ReturnType<typeof useAsyncData<TData | null, TError>> 142 + : TComposable extends 'useFetch' 143 + ? ReturnType<typeof useFetch<TData | null, TError>> 144 + : TComposable extends 'useLazyAsyncData' 145 + ? ReturnType<typeof useLazyAsyncData<TData | null, TError>> 146 + : TComposable extends 'useLazyFetch' 147 + ? ReturnType<typeof useLazyFetch<TData | null, TError>> 148 + : never; 180 149 181 150 type MethodFn = < 151 + TComposable extends Composable, 182 152 TData = unknown, 183 153 TError = unknown, 184 - TComposable extends Composable = Composable, 185 154 >( 186 - options: Omit<Options<TData, TError, TComposable>, 'method'> & { 187 - url: string; 188 - }, 189 - ) => RequestResultNuxt<TData, TError, TComposable>; 155 + options: Omit<RequestOptions<TComposable>, 'method'>, 156 + ) => RequestResult<TComposable, TData, TError>; 190 157 191 158 type RequestFn = < 192 - Data = unknown, 159 + TComposable extends Composable, 160 + TData = unknown, 193 161 TError = unknown, 194 - ThrowOnError extends boolean = false, 195 162 >( 196 - options: Omit<RequestOptions<ThrowOnError>, 'method'> & 197 - Pick<Required<RequestOptions<ThrowOnError>>, 'method'>, 198 - ) => RequestResult<Data, TError, ThrowOnError>; 199 - 200 - export type Composable = 201 - | '$fetch' 202 - | 'useAsyncData' 203 - | 'useFetch' 204 - | 'useLazyAsyncData' 205 - | 'useLazyFetch'; 206 - 207 - // eslint-disable-next-line @typescript-eslint/no-unused-vars 208 - export type Options<TData, TError, TComposable extends Composable> = Parameters< 209 - typeof $fetch 210 - >['1'] & 211 - Pick<RequestOptions<false>, 'client'> & { 212 - asyncDataOptions?: AsyncDataOptions<TData>; 213 - composable: TComposable; 214 - fetchOptions?: UseFetchOptions<TData, TData>; 215 - key?: string; 216 - requestFetch?: ReturnType<typeof useRequestFetch>; 217 - }; 163 + options: Omit<RequestOptions<TComposable>, 'method'> & 164 + Pick<Required<RequestOptions<TComposable>>, 'method'>, 165 + ) => RequestResult<TComposable, TData, TError>; 218 166 219 - type RequestResultNuxt<TData, TError, TComposable> = 220 - TComposable extends '$fetch' 221 - ? ReturnType<typeof $fetch<TData>> 222 - : TComposable extends 'useAsyncData' 223 - ? ReturnType<typeof useAsyncData<TData | null, TError>> 224 - : TComposable extends 'useFetch' 225 - ? ReturnType<typeof useFetch<TData | null, TError>> 226 - : TComposable extends 'useLazyAsyncData' 227 - ? ReturnType<typeof useLazyAsyncData<TData | null, TError>> 228 - : TComposable extends 'useLazyFetch' 229 - ? ReturnType<typeof useLazyFetch<TData | null, TError>> 230 - : never; 231 - 232 - type ClientRequestFn = <TData, TError, TComposable extends Composable>( 233 - options: Options<TData, TError, TComposable> & { 234 - url: string; 235 - }, 236 - ) => RequestResultNuxt<TData, TError, TComposable>; 237 - 238 - export interface Client< 239 - Req = Request, 240 - Res = Response, 241 - Err = unknown, 242 - Opts = RequestOptions, 243 - > { 167 + export interface Client { 244 168 /** 245 169 * Returns the final request URL. This method works only with experimental parser. 246 170 */ 247 171 buildUrl: < 248 - Data extends { 172 + TData extends { 249 173 body?: unknown; 250 174 path?: Record<string, unknown>; 251 - query?: Record<string, unknown>; 175 + query?: FetchOptions<unknown>['query']; 252 176 url: string; 253 177 }, 254 178 >( 255 - options: Pick<Data, 'url'> & OptionsOld<Data>, 179 + options: Pick<TData, 'path' | 'query' | 'url'> & 180 + Pick<Options<'$fetch', TData>, 'baseURL' | 'querySerializer'>, 256 181 ) => string; 257 - clientRequest: ClientRequestFn; 258 182 connect: MethodFn; 259 183 delete: MethodFn; 260 184 get: MethodFn; 261 185 getConfig: () => Config; 262 186 head: MethodFn; 263 - interceptors: Middleware<Req, Res, Err, Opts>; 264 187 options: MethodFn; 265 188 patch: MethodFn; 266 189 post: MethodFn; ··· 278 201 url: string; 279 202 } 280 203 281 - export type OptionsOld< 282 - Data extends DataShape = DataShape, 283 - ThrowOnError extends boolean = boolean, 284 - > = OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'path' | 'query' | 'url'> & 285 - Omit<Data, 'url'>; 204 + export type Options< 205 + TComposable extends Composable, 206 + TData extends DataShape = DataShape, 207 + > = OmitKeys<RequestOptions<TComposable>, 'body' | 'path' | 'query' | 'url'> & 208 + Omit<TData, 'url'>; 209 + 210 + export type OptionsLegacyParser<TData = unknown> = TData extends { body?: any } 211 + ? TData extends { headers?: any } 212 + ? OmitKeys<RequestOptions, 'body' | 'headers' | 'url'> & TData 213 + : OmitKeys<RequestOptions, 'body' | 'url'> & 214 + TData & 215 + Pick<RequestOptions, 'headers'> 216 + : TData extends { headers?: any } 217 + ? OmitKeys<RequestOptions, 'headers' | 'url'> & 218 + TData & 219 + Pick<RequestOptions, 'body'> 220 + : OmitKeys<RequestOptions, 'url'> & TData; 221 + 222 + type FetchOptions<TData> = Omit< 223 + UseFetchOptions<TData, TData>, 224 + keyof AsyncDataOptions<TData> 225 + >; 286 226 287 - export type OptionsLegacyParser< 288 - Data = unknown, 289 - ThrowOnError extends boolean = boolean, 290 - > = Data extends { body?: any } 291 - ? Data extends { headers?: any } 292 - ? OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'headers' | 'url'> & Data 293 - : OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'url'> & 294 - Data & 295 - Pick<RequestOptions<ThrowOnError>, 'headers'> 296 - : Data extends { headers?: any } 297 - ? OmitKeys<RequestOptions<ThrowOnError>, 'headers' | 'url'> & 298 - Data & 299 - Pick<RequestOptions<ThrowOnError>, 'body'> 300 - : OmitKeys<RequestOptions<ThrowOnError>, 'url'> & Data; 227 + export type Composable = 228 + | '$fetch' 229 + | 'useAsyncData' 230 + | 'useFetch' 231 + | 'useLazyAsyncData' 232 + | 'useLazyFetch';
+38 -138
packages/client-nuxt/src/utils.ts
··· 1 - import type { Client, Config, RequestOptions, Security } from './types'; 1 + import type { Auth, Client, Config, RequestOptions } from './types'; 2 2 3 3 interface PathSerializer { 4 4 path: Record<string, unknown>; ··· 13 13 type ObjectStyle = 'form' | 'deepObject'; 14 14 type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; 15 15 16 - export type QuerySerializer = (query: Record<string, unknown>) => string; 16 + export type QuerySerializer = ( 17 + query: Parameters<Client['buildUrl']>[0]['query'], 18 + ) => string; 17 19 18 20 export type BodySerializer = (body: any) => any; 19 21 ··· 322 324 return querySerializer; 323 325 }; 324 326 325 - /** 326 - * Infers parseAs value from provided Content-Type header. 327 - */ 328 - export const getParseAs = ( 329 - contentType: string | null, 330 - ): Exclude<Config['parseAs'], 'auto'> => { 331 - if (!contentType) { 332 - // If no Content-Type header is provided, the best we can do is return the raw response body, 333 - // which is effectively the same as the 'stream' option. 334 - return 'stream'; 335 - } 327 + export const getAuthToken = async ( 328 + auth: Auth, 329 + callback: RequestOptions['auth'], 330 + ): Promise<string | undefined> => { 331 + const token = 332 + typeof callback === 'function' ? await callback(auth) : callback; 336 333 337 - const cleanContent = contentType.split(';')[0]?.trim(); 338 - 339 - if (!cleanContent) { 334 + if (!token) { 340 335 return; 341 336 } 342 337 343 - if ( 344 - cleanContent.startsWith('application/json') || 345 - cleanContent.endsWith('+json') 346 - ) { 347 - return 'json'; 348 - } 349 - 350 - if (cleanContent === 'multipart/form-data') { 351 - return 'formData'; 352 - } 353 - 354 - if ( 355 - ['application/', 'audio/', 'image/', 'video/'].some((type) => 356 - cleanContent.startsWith(type), 357 - ) 358 - ) { 359 - return 'blob'; 360 - } 361 - 362 - if (cleanContent.startsWith('text/')) { 363 - return 'text'; 338 + if (auth.scheme === 'bearer') { 339 + return `Bearer ${token}`; 364 340 } 365 - }; 366 341 367 - export const getAuthToken = async ( 368 - security: Security, 369 - options: Pick<RequestOptions, 'accessToken' | 'apiKey'>, 370 - ): Promise<string | undefined> => { 371 - if (security.fn === 'accessToken') { 372 - const token = 373 - typeof options.accessToken === 'function' 374 - ? await options.accessToken() 375 - : options.accessToken; 376 - return token ? `Bearer ${token}` : undefined; 342 + if (auth.scheme === 'basic') { 343 + return `Basic ${btoa(token)}`; 377 344 } 378 345 379 - if (security.fn === 'apiKey') { 380 - return typeof options.apiKey === 'function' 381 - ? await options.apiKey() 382 - : options.apiKey; 383 - } 346 + return token; 384 347 }; 385 348 386 349 export const setAuthParams = async ({ 387 350 security, 388 351 ...options 389 352 }: Pick<Required<RequestOptions>, 'security'> & 390 - Pick<RequestOptions, 'accessToken' | 'apiKey' | 'query'> & { 353 + Pick<RequestOptions, 'auth' | 'query'> & { 391 354 headers: Headers; 392 355 }) => { 393 - for (const scheme of security) { 394 - const token = await getAuthToken(scheme, options); 356 + for (const auth of security) { 357 + const token = await getAuthToken(auth, options.auth); 395 358 396 359 if (!token) { 397 360 continue; 398 361 } 399 362 400 - if (scheme.in === 'header') { 401 - options.headers.set(scheme.name, token); 402 - } else if (scheme.in === 'query') { 403 - if (!options.query) { 404 - options.query = {}; 405 - } 363 + const name = auth.name ?? 'Authorization'; 406 364 407 - options.query[scheme.name] = token; 365 + switch (auth.in) { 366 + case 'query': 367 + if (!options.query) { 368 + options.query = {}; 369 + } 370 + // TODO: handle refs 371 + // @ts-expect-error 372 + options.query[name] = token; 373 + break; 374 + case 'header': 375 + default: 376 + options.headers.set(name, token); 377 + break; 408 378 } 409 379 410 380 return; ··· 413 383 414 384 export const buildUrl: Client['buildUrl'] = (options) => { 415 385 const url = getUrl({ 416 - baseUrl: options.baseUrl ?? '', 386 + baseUrl: options.baseURL ?? '', 417 387 path: options.path, 418 388 query: options.query, 419 389 querySerializer: ··· 431 401 query, 432 402 querySerializer, 433 403 url: _url, 434 - }: { 404 + }: Pick<Parameters<Client['buildUrl']>[0], 'path' | 'query' | 'url'> & { 435 405 baseUrl: string; 436 - path?: Record<string, unknown>; 437 - query?: Record<string, unknown>; 438 406 querySerializer: QuerySerializer; 439 - url: string; 440 407 }) => { 441 408 const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; 442 409 let url = baseUrl + pathUrl; ··· 455 422 456 423 export const mergeConfigs = (a: Config, b: Config): Config => { 457 424 const config = { ...a, ...b }; 458 - if (config.baseUrl?.endsWith('/')) { 459 - config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); 425 + if (config.baseURL?.endsWith('/')) { 426 + config.baseURL = config.baseURL.substring(0, config.baseURL.length - 1); 460 427 } 461 428 config.headers = mergeHeaders(a.headers, b.headers); 462 429 return config; ··· 494 461 return mergedHeaders; 495 462 }; 496 463 497 - type ErrInterceptor<Err, Res, Req, Options> = ( 498 - error: Err, 499 - response: Res, 500 - request: Req, 501 - options: Options, 502 - ) => Err | Promise<Err>; 503 - 504 - type ReqInterceptor<Req, Options> = ( 505 - request: Req, 506 - options: Options, 507 - ) => Req | Promise<Req>; 508 - 509 - type ResInterceptor<Res, Req, Options> = ( 510 - response: Res, 511 - request: Req, 512 - options: Options, 513 - ) => Res | Promise<Res>; 514 - 515 - class Interceptors<Interceptor> { 516 - _fns: Interceptor[]; 517 - 518 - constructor() { 519 - this._fns = []; 520 - } 521 - 522 - clear() { 523 - this._fns = []; 524 - } 525 - 526 - exists(fn: Interceptor) { 527 - return this._fns.indexOf(fn) !== -1; 528 - } 529 - 530 - eject(fn: Interceptor) { 531 - const index = this._fns.indexOf(fn); 532 - if (index !== -1) { 533 - this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; 534 - } 535 - } 536 - 537 - use(fn: Interceptor) { 538 - this._fns = [...this._fns, fn]; 539 - } 540 - } 541 - 542 - // `createInterceptors()` response, meant for external use as it does not 543 - // expose internals 544 - export interface Middleware<Req, Res, Err, Options> { 545 - error: Pick< 546 - Interceptors<ErrInterceptor<Err, Res, Req, Options>>, 547 - 'eject' | 'use' 548 - >; 549 - request: Pick<Interceptors<ReqInterceptor<Req, Options>>, 'eject' | 'use'>; 550 - response: Pick< 551 - Interceptors<ResInterceptor<Res, Req, Options>>, 552 - 'eject' | 'use' 553 - >; 554 - } 555 - 556 - // do not add `Middleware` as return type so we can use _fns internally 557 - export const createInterceptors = <Req, Res, Err, Options>() => ({ 558 - error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(), 559 - request: new Interceptors<ReqInterceptor<Req, Options>>(), 560 - response: new Interceptors<ResInterceptor<Res, Req, Options>>(), 561 - }); 562 - 563 464 const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { 564 465 if (typeof value === 'string' || value instanceof Blob) { 565 466 data.append(key, value); ··· 644 545 645 546 export const createConfig = (override: Config = {}): Config => ({ 646 547 ...jsonBodySerializer, 647 - baseUrl: '', 548 + baseURL: '', 648 549 headers: defaultHeaders, 649 - parseAs: 'auto', 650 550 querySerializer: defaultQuerySerializer, 651 551 ...override, 652 552 });
+14 -13
packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts
··· 35 35 type: 'apiKey' | 'http'; 36 36 } 37 37 38 + const nuxtTypeComposable = 'TComposable'; 39 + 38 40 export const operationOptionsType = ({ 39 41 context, 40 42 file, ··· 51 53 const optionsName = clientApi.Options.name; 52 54 53 55 if (context.config.client.name === '@hey-api/client-nuxt') { 54 - const identifierError = importIdentifierError({ context, file, operation }); 55 - return `${optionsName}<${identifierData?.name || 'unknown'}, ${identifierError?.name || 'unknown'}, TComposable>`; 56 + return `${optionsName}<${nuxtTypeComposable}, ${identifierData.name || 'unknown'}>`; 56 57 } 57 58 58 59 // TODO: refactor this to be more generic, works for now 59 60 if (throwOnError) { 60 - return `${optionsName}<${identifierData?.name || 'unknown'}, ${throwOnError}>`; 61 + return `${optionsName}<${identifierData.name || 'unknown'}, ${throwOnError}>`; 61 62 } 62 - return identifierData 63 + return identifierData.name 63 64 ? `${optionsName}<${identifierData.name}>` 64 65 : optionsName; 65 66 }; ··· 424 425 }); 425 426 426 427 const isNuxtClient = context.config.client.name === '@hey-api/client-nuxt'; 428 + const responseType = identifierResponse.name || 'unknown'; 429 + const errorType = identifierError.name || 'unknown'; 427 430 428 431 return [ 429 432 compiler.returnFunctionCall({ ··· 434 437 }), 435 438 ], 436 439 name: `(options?.client ?? client).${operation.method}`, 437 - types: [ 438 - identifierResponse.name || 'unknown', 439 - identifierError.name || 'unknown', 440 - isNuxtClient ? 'TComposable' : 'ThrowOnError', 441 - ], 440 + types: isNuxtClient 441 + ? [nuxtTypeComposable, responseType, errorType] 442 + : [responseType, errorType, 'ThrowOnError'], 442 443 }), 443 444 ]; 444 445 }; ··· 471 472 }), 472 473 parameters: [ 473 474 { 474 - isRequired: hasOperationDataRequired(operation), 475 + isRequired: isNuxtClient || hasOperationDataRequired(operation), 475 476 name: 'options', 476 477 type: operationOptionsType({ 477 478 context, ··· 491 492 ? [ 492 493 { 493 494 extends: compiler.typeNode('Composable'), 494 - name: 'TComposable', 495 + name: nuxtTypeComposable, 495 496 }, 496 497 ] 497 498 : [ ··· 552 553 expression: compiler.arrowFunction({ 553 554 parameters: [ 554 555 { 555 - isRequired: hasOperationDataRequired(operation), 556 + isRequired: isNuxtClient || hasOperationDataRequired(operation), 556 557 name: 'options', 557 558 type: operationOptionsType({ 558 559 context, ··· 572 573 ? [ 573 574 { 574 575 extends: compiler.typeNode('Composable'), 575 - name: 'TComposable', 576 + name: nuxtTypeComposable, 576 577 }, 577 578 ] 578 579 : [