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

chore: add transformer and validator

Lubos 848f34ce ca013b35

+417 -173
+60
examples/openapi-ts-nuxt/client/sdk.gen.ts
··· 7 7 type Options, 8 8 } from '@hey-api/client-nuxt'; 9 9 10 + import { 11 + addPetResponseTransformer, 12 + createUserResponseTransformer, 13 + findPetsByStatusResponseTransformer, 14 + findPetsByTagsResponseTransformer, 15 + getOrderByIdResponseTransformer, 16 + getPetByIdResponseTransformer, 17 + getUserByNameResponseTransformer, 18 + placeOrderResponseTransformer, 19 + updatePetResponseTransformer, 20 + } from './transformers.gen'; 10 21 import type { 11 22 AddPetData, 12 23 AddPetResponse, ··· 41 52 UploadFileData, 42 53 UploadFileResponse, 43 54 } from './types.gen'; 55 + import { 56 + zAddPetResponse, 57 + zCreateUserResponse, 58 + zCreateUsersWithListInputResponse, 59 + zFindPetsByStatusResponse, 60 + zFindPetsByTagsResponse, 61 + zGetInventoryResponse, 62 + zGetOrderByIdResponse, 63 + zGetPetByIdResponse, 64 + zGetUserByNameResponse, 65 + zLoginUserResponse, 66 + zPlaceOrderResponse, 67 + zUpdatePetResponse, 68 + zUploadFileResponse, 69 + } from './zod.gen'; 44 70 45 71 export const client = createClient(createConfig()); 46 72 ··· 57 83 'Content-Type': 'application/json', 58 84 ...options?.headers, 59 85 }, 86 + responseTransformer: addPetResponseTransformer, 87 + responseValidator: async (data) => await zAddPetResponse.parseAsync(data), 60 88 url: '/pet', 61 89 }); 62 90 ··· 73 101 'Content-Type': 'application/json', 74 102 ...options?.headers, 75 103 }, 104 + responseTransformer: updatePetResponseTransformer, 105 + responseValidator: async (data) => 106 + await zUpdatePetResponse.parseAsync(data), 76 107 url: '/pet', 77 108 }); 78 109 ··· 89 120 unknown 90 121 >({ 91 122 ...options, 123 + responseTransformer: findPetsByStatusResponseTransformer, 124 + responseValidator: async (data) => 125 + await zFindPetsByStatusResponse.parseAsync(data), 92 126 url: '/pet/findByStatus', 93 127 }); 94 128 ··· 102 136 (options?.client ?? client).get<TComposable, FindPetsByTagsResponse, unknown>( 103 137 { 104 138 ...options, 139 + responseTransformer: findPetsByTagsResponseTransformer, 140 + responseValidator: async (data) => 141 + await zFindPetsByTagsResponse.parseAsync(data), 105 142 url: '/pet/findByTags', 106 143 }, 107 144 ); ··· 126 163 ) => 127 164 (options?.client ?? client).get<TComposable, GetPetByIdResponse, unknown>({ 128 165 ...options, 166 + responseTransformer: getPetByIdResponseTransformer, 167 + responseValidator: async (data) => 168 + await zGetPetByIdResponse.parseAsync(data), 129 169 security: [ 130 170 { 131 171 name: 'api_key', ··· 158 198 'Content-Type': 'application/octet-stream', 159 199 ...options?.headers, 160 200 }, 201 + responseValidator: async (data) => 202 + await zUploadFileResponse.parseAsync(data), 161 203 url: '/pet/{petId}/uploadImage', 162 204 }); 163 205 ··· 170 212 ) => 171 213 (options?.client ?? client).get<TComposable, GetInventoryResponse, unknown>({ 172 214 ...options, 215 + responseValidator: async (data) => 216 + await zGetInventoryResponse.parseAsync(data), 173 217 security: [ 174 218 { 175 219 name: 'api_key', ··· 192 236 'Content-Type': 'application/json', 193 237 ...options?.headers, 194 238 }, 239 + responseTransformer: placeOrderResponseTransformer, 240 + responseValidator: async (data) => 241 + await zPlaceOrderResponse.parseAsync(data), 195 242 url: '/store/order', 196 243 }); 197 244 ··· 216 263 ) => 217 264 (options?.client ?? client).get<TComposable, GetOrderByIdResponse, unknown>({ 218 265 ...options, 266 + responseTransformer: getOrderByIdResponseTransformer, 267 + responseValidator: async (data) => 268 + await zGetOrderByIdResponse.parseAsync(data), 219 269 url: '/store/order/{orderId}', 220 270 }); 221 271 ··· 232 282 'Content-Type': 'application/json', 233 283 ...options?.headers, 234 284 }, 285 + responseTransformer: createUserResponseTransformer, 286 + responseValidator: async (data) => 287 + await zCreateUserResponse.parseAsync(data), 235 288 url: '/user', 236 289 }); 237 290 ··· 252 305 'Content-Type': 'application/json', 253 306 ...options?.headers, 254 307 }, 308 + responseValidator: async (data) => 309 + await zCreateUsersWithListInputResponse.parseAsync(data), 255 310 url: '/user/createWithList', 256 311 }); 257 312 ··· 263 318 ) => 264 319 (options?.client ?? client).get<TComposable, LoginUserResponse, unknown>({ 265 320 ...options, 321 + responseValidator: async (data) => 322 + await zLoginUserResponse.parseAsync(data), 266 323 url: '/user/login', 267 324 }); 268 325 ··· 297 354 ) => 298 355 (options?.client ?? client).get<TComposable, GetUserByNameResponse, unknown>({ 299 356 ...options, 357 + responseTransformer: getUserByNameResponseTransformer, 358 + responseValidator: async (data) => 359 + await zGetUserByNameResponse.parseAsync(data), 300 360 url: '/user/{username}', 301 361 }); 302 362
+125
examples/openapi-ts-nuxt/client/transformers.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import type { 4 + AddPetResponse, 5 + CreateUserResponse, 6 + FindPetsByStatusResponse, 7 + FindPetsByTagsResponse, 8 + GetOrderByIdResponse, 9 + GetPetByIdResponse, 10 + GetUserByNameResponse, 11 + PlaceOrderResponse, 12 + UpdatePetResponse, 13 + } from './types.gen'; 14 + 15 + const categorySchemaResponseTransformer = (data: any) => { 16 + if (data.id) { 17 + data.id = BigInt(data.id.toString()); 18 + } 19 + return data; 20 + }; 21 + 22 + const tagSchemaResponseTransformer = (data: any) => { 23 + if (data.id) { 24 + data.id = BigInt(data.id.toString()); 25 + } 26 + return data; 27 + }; 28 + 29 + const petSchemaResponseTransformer = (data: any) => { 30 + if (data.id) { 31 + data.id = BigInt(data.id.toString()); 32 + } 33 + if (data.category) { 34 + data.category = categorySchemaResponseTransformer(data.category); 35 + } 36 + if (data.tags) { 37 + data.tags = data.tags.map((item: any) => 38 + tagSchemaResponseTransformer(item), 39 + ); 40 + } 41 + return data; 42 + }; 43 + 44 + export const addPetResponseTransformer = async ( 45 + data: any, 46 + ): Promise<AddPetResponse> => { 47 + data = petSchemaResponseTransformer(data); 48 + return data; 49 + }; 50 + 51 + export const updatePetResponseTransformer = async ( 52 + data: any, 53 + ): Promise<UpdatePetResponse> => { 54 + data = petSchemaResponseTransformer(data); 55 + return data; 56 + }; 57 + 58 + export const findPetsByStatusResponseTransformer = async ( 59 + data: any, 60 + ): Promise<FindPetsByStatusResponse> => { 61 + data = data.map((item: any) => petSchemaResponseTransformer(item)); 62 + return data; 63 + }; 64 + 65 + export const findPetsByTagsResponseTransformer = async ( 66 + data: any, 67 + ): Promise<FindPetsByTagsResponse> => { 68 + data = data.map((item: any) => petSchemaResponseTransformer(item)); 69 + return data; 70 + }; 71 + 72 + export const getPetByIdResponseTransformer = async ( 73 + data: any, 74 + ): Promise<GetPetByIdResponse> => { 75 + data = petSchemaResponseTransformer(data); 76 + return data; 77 + }; 78 + 79 + const orderSchemaResponseTransformer = (data: any) => { 80 + if (data.id) { 81 + data.id = BigInt(data.id.toString()); 82 + } 83 + if (data.petId) { 84 + data.petId = BigInt(data.petId.toString()); 85 + } 86 + if (data.shipDate) { 87 + data.shipDate = new Date(data.shipDate); 88 + } 89 + return data; 90 + }; 91 + 92 + export const placeOrderResponseTransformer = async ( 93 + data: any, 94 + ): Promise<PlaceOrderResponse> => { 95 + data = orderSchemaResponseTransformer(data); 96 + return data; 97 + }; 98 + 99 + export const getOrderByIdResponseTransformer = async ( 100 + data: any, 101 + ): Promise<GetOrderByIdResponse> => { 102 + data = orderSchemaResponseTransformer(data); 103 + return data; 104 + }; 105 + 106 + const userSchemaResponseTransformer = (data: any) => { 107 + if (data.id) { 108 + data.id = BigInt(data.id.toString()); 109 + } 110 + return data; 111 + }; 112 + 113 + export const createUserResponseTransformer = async ( 114 + data: any, 115 + ): Promise<CreateUserResponse> => { 116 + data = userSchemaResponseTransformer(data); 117 + return data; 118 + }; 119 + 120 + export const getUserByNameResponseTransformer = async ( 121 + data: any, 122 + ): Promise<GetUserByNameResponse> => { 123 + data = userSchemaResponseTransformer(data); 124 + return data; 125 + };
+14 -14
examples/openapi-ts-nuxt/client/types.gen.ts
··· 2 2 3 3 export type Order = { 4 4 complete?: boolean; 5 - id?: number; 6 - petId?: number; 5 + id?: bigint; 6 + petId?: bigint; 7 7 quantity?: number; 8 - shipDate?: string; 8 + shipDate?: Date; 9 9 /** 10 10 * Order Status 11 11 */ ··· 14 14 15 15 export type Customer = { 16 16 address?: Array<Address>; 17 - id?: number; 17 + id?: bigint; 18 18 username?: string; 19 19 }; 20 20 ··· 26 26 }; 27 27 28 28 export type Category = { 29 - id?: number; 29 + id?: bigint; 30 30 name?: string; 31 31 }; 32 32 33 33 export type User = { 34 34 email?: string; 35 35 firstName?: string; 36 - id?: number; 36 + id?: bigint; 37 37 lastName?: string; 38 38 password?: string; 39 39 phone?: string; ··· 45 45 }; 46 46 47 47 export type Tag = { 48 - id?: number; 48 + id?: bigint; 49 49 name?: string; 50 50 }; 51 51 52 52 export type Pet = { 53 53 category?: Category; 54 - id?: number; 54 + id?: bigint; 55 55 name: string; 56 56 photoUrls: Array<string>; 57 57 /** ··· 201 201 /** 202 202 * Pet id to delete 203 203 */ 204 - petId: number; 204 + petId: bigint; 205 205 }; 206 206 query?: never; 207 207 url: '/pet/{petId}'; ··· 220 220 /** 221 221 * ID of pet to return 222 222 */ 223 - petId: number; 223 + petId: bigint; 224 224 }; 225 225 query?: never; 226 226 url: '/pet/{petId}'; ··· 252 252 /** 253 253 * ID of pet that needs to be updated 254 254 */ 255 - petId: number; 255 + petId: bigint; 256 256 }; 257 257 query?: { 258 258 /** ··· 280 280 /** 281 281 * ID of pet to update 282 282 */ 283 - petId: number; 283 + petId: bigint; 284 284 }; 285 285 query?: { 286 286 /** ··· 348 348 /** 349 349 * ID of the order that needs to be deleted 350 350 */ 351 - orderId: number; 351 + orderId: bigint; 352 352 }; 353 353 query?: never; 354 354 url: '/store/order/{orderId}'; ··· 371 371 /** 372 372 * ID of order that needs to be fetched 373 373 */ 374 - orderId: number; 374 + orderId: bigint; 375 375 }; 376 376 query?: never; 377 377 url: '/store/order/{orderId}';
+96
examples/openapi-ts-nuxt/client/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zOrder = z.object({ 6 + complete: z.boolean().optional(), 7 + id: z.coerce.bigint().optional(), 8 + petId: z.coerce.bigint().optional(), 9 + quantity: z.number().int().optional(), 10 + shipDate: z.string().datetime().optional(), 11 + status: z.enum(['placed', 'approved', 'delivered']).optional(), 12 + }); 13 + 14 + export const zCustomer = z.object({ 15 + address: z 16 + .array( 17 + z.object({ 18 + city: z.string().optional(), 19 + state: z.string().optional(), 20 + street: z.string().optional(), 21 + zip: z.string().optional(), 22 + }), 23 + ) 24 + .optional(), 25 + id: z.coerce.bigint().optional(), 26 + username: z.string().optional(), 27 + }); 28 + 29 + export const zAddress = z.object({ 30 + city: z.string().optional(), 31 + state: z.string().optional(), 32 + street: z.string().optional(), 33 + zip: z.string().optional(), 34 + }); 35 + 36 + export const zCategory = z.object({ 37 + id: z.coerce.bigint().optional(), 38 + name: z.string().optional(), 39 + }); 40 + 41 + export const zUser = z.object({ 42 + email: z.string().optional(), 43 + firstName: z.string().optional(), 44 + id: z.coerce.bigint().optional(), 45 + lastName: z.string().optional(), 46 + password: z.string().optional(), 47 + phone: z.string().optional(), 48 + userStatus: z.number().int().optional(), 49 + username: z.string().optional(), 50 + }); 51 + 52 + export const zTag = z.object({ 53 + id: z.coerce.bigint().optional(), 54 + name: z.string().optional(), 55 + }); 56 + 57 + export const zPet = z.object({ 58 + category: zCategory.optional(), 59 + id: z.coerce.bigint().optional(), 60 + name: z.string(), 61 + photoUrls: z.array(z.string()), 62 + status: z.enum(['available', 'pending', 'sold']).optional(), 63 + tags: z.array(zTag).optional(), 64 + }); 65 + 66 + export const zApiResponse = z.object({ 67 + code: z.number().int().optional(), 68 + message: z.string().optional(), 69 + type: z.string().optional(), 70 + }); 71 + 72 + export const zAddPetResponse = zPet; 73 + 74 + export const zUpdatePetResponse = zPet; 75 + 76 + export const zFindPetsByStatusResponse = z.array(zPet); 77 + 78 + export const zFindPetsByTagsResponse = z.array(zPet); 79 + 80 + export const zGetPetByIdResponse = zPet; 81 + 82 + export const zUploadFileResponse = zApiResponse; 83 + 84 + export const zGetInventoryResponse = z.object({}); 85 + 86 + export const zPlaceOrderResponse = zOrder; 87 + 88 + export const zGetOrderByIdResponse = zOrder; 89 + 90 + export const zCreateUserResponse = zUser; 91 + 92 + export const zCreateUsersWithListInputResponse = z.union([zUser, z.unknown()]); 93 + 94 + export const zLoginUserResponse = z.string(); 95 + 96 + export const zGetUserByNameResponse = zUser;
+6 -6
examples/openapi-ts-nuxt/components/home.vue
··· 15 15 composable: 'useAsyncData', 16 16 key: 'item', 17 17 path: { 18 - petId: 8, 18 + petId: BigInt(8), 19 19 }, 20 20 }); 21 21 ··· 30 30 $fetch: requestFetch, 31 31 composable: 'useAsyncData', 32 32 path: { 33 - petId: 8, 33 + petId: BigInt(8), 34 34 }, 35 35 }); 36 36 ··· 42 42 const fetch = await getPetById({ 43 43 composable: 'useFetch', 44 44 path: { 45 - petId: 8, 45 + petId: BigInt(8), 46 46 }, 47 47 }); 48 48 ··· 56 56 composable: 'useLazyAsyncData', 57 57 key: 'count', 58 58 path: { 59 - petId: 8, 59 + petId: BigInt(8), 60 60 }, 61 61 }); 62 62 watch(lazyAsyncData.data, (newPet) => { ··· 76 76 const lazyFetch = await getPetById({ 77 77 composable: 'useLazyFetch', 78 78 path: { 79 - petId: 8, 79 + petId: BigInt(8), 80 80 }, 81 81 }); 82 82 watch(lazyFetch.data, (newPet) => { ··· 91 91 const result = await getPetById({ 92 92 composable: '$fetch', 93 93 path: { 94 - petId: 8, 94 + petId: BigInt(8), 95 95 }, 96 96 }); 97 97 console.log(result);
+7 -1
examples/openapi-ts-nuxt/openapi-ts.config.ts
··· 12 12 }, 13 13 plugins: [ 14 14 '@hey-api/schemas', 15 - '@hey-api/sdk', 15 + { 16 + name: '@hey-api/sdk', 17 + transformer: true, 18 + validator: true, 19 + }, 16 20 { 17 21 enums: 'javascript', 18 22 name: '@hey-api/typescript', 19 23 }, 24 + '@hey-api/transformers', 25 + 'zod', 20 26 ], 21 27 });
+2 -1
examples/openapi-ts-nuxt/package.json
··· 16 16 "@hey-api/client-nuxt": "workspace:*", 17 17 "nuxt": "3.15.1", 18 18 "vue": "3.5.13", 19 - "vue-router": "4.5.0" 19 + "vue-router": "4.5.0", 20 + "zod": "3.23.8" 20 21 }, 21 22 "devDependencies": { 22 23 "@hey-api/openapi-ts": "workspace:*"
+72 -134
packages/client-nuxt/src/__tests__/utils.test.ts
··· 1 1 import { describe, expect, it, vi } from 'vitest'; 2 2 3 - import { getAuthToken, getParseAs, setAuthParams } from '../utils'; 3 + import type { Auth } from '../types'; 4 + import { getAuthToken, setAuthParams } from '../utils'; 4 5 5 6 describe('getAuthToken', () => { 6 - it('returns access token', async () => { 7 - const accessToken = vi.fn().mockReturnValue('foo'); 8 - const apiKey = vi.fn().mockReturnValue('bar'); 7 + it('returns bearer token', async () => { 8 + const auth = vi.fn().mockReturnValue('foo'); 9 9 const token = await getAuthToken( 10 10 { 11 - fn: 'accessToken', 12 - in: 'header', 13 - name: 'baz', 14 - }, 15 - { 16 - accessToken, 17 - apiKey, 11 + scheme: 'bearer', 12 + type: 'http', 18 13 }, 14 + auth, 19 15 ); 20 - expect(accessToken).toHaveBeenCalled(); 16 + expect(auth).toHaveBeenCalled(); 21 17 expect(token).toBe('Bearer foo'); 22 18 }); 23 19 24 - it('returns nothing when accessToken function is undefined', async () => { 25 - const apiKey = vi.fn().mockReturnValue('bar'); 20 + it('returns basic token', async () => { 21 + const auth = vi.fn().mockReturnValue('foo:bar'); 26 22 const token = await getAuthToken( 27 23 { 28 - fn: 'accessToken', 29 - in: 'header', 30 - name: 'baz', 24 + scheme: 'basic', 25 + type: 'http', 31 26 }, 32 - { 33 - apiKey, 34 - }, 27 + auth, 35 28 ); 36 - expect(token).toBeUndefined(); 29 + expect(auth).toHaveBeenCalled(); 30 + expect(token).toBe(`Basic ${btoa('foo:bar')}`); 37 31 }); 38 32 39 - it('returns API key', async () => { 40 - const accessToken = vi.fn().mockReturnValue('foo'); 41 - const apiKey = vi.fn().mockReturnValue('bar'); 33 + it('returns raw token', async () => { 34 + const auth = vi.fn().mockReturnValue('foo'); 42 35 const token = await getAuthToken( 43 36 { 44 - fn: 'apiKey', 45 - in: 'header', 46 - name: 'baz', 37 + type: 'http', 47 38 }, 48 - { 49 - accessToken, 50 - apiKey, 51 - }, 39 + auth, 52 40 ); 53 - expect(apiKey).toHaveBeenCalled(); 54 - expect(token).toBe('bar'); 41 + expect(auth).toHaveBeenCalled(); 42 + expect(token).toBe('foo'); 55 43 }); 56 44 57 - it('returns nothing when apiKey function is undefined', async () => { 58 - const accessToken = vi.fn().mockReturnValue('foo'); 45 + it('returns nothing when auth function is undefined', async () => { 59 46 const token = await getAuthToken( 60 47 { 61 - fn: 'apiKey', 62 - in: 'header', 63 - name: 'baz', 48 + type: 'http', 64 49 }, 65 - { 66 - accessToken, 67 - }, 50 + undefined, 68 51 ); 69 52 expect(token).toBeUndefined(); 70 53 }); 71 54 }); 72 55 73 - describe('getParseAs', () => { 74 - const scenarios: Array<{ 75 - content: Parameters<typeof getParseAs>[0]; 76 - parseAs: ReturnType<typeof getParseAs>; 77 - }> = [ 78 - { 79 - content: null, 80 - parseAs: 'stream', 81 - }, 82 - { 83 - content: 'application/json', 84 - parseAs: 'json', 85 - }, 86 - { 87 - content: 'application/ld+json', 88 - parseAs: 'json', 89 - }, 90 - { 91 - content: 'application/ld+json;charset=utf-8', 92 - parseAs: 'json', 93 - }, 94 - { 95 - content: 'application/ld+json; charset=utf-8', 96 - parseAs: 'json', 97 - }, 98 - { 99 - content: 'multipart/form-data', 100 - parseAs: 'formData', 101 - }, 102 - { 103 - content: 'application/*', 104 - parseAs: 'blob', 105 - }, 106 - { 107 - content: 'audio/*', 108 - parseAs: 'blob', 109 - }, 110 - { 111 - content: 'image/*', 112 - parseAs: 'blob', 113 - }, 114 - { 115 - content: 'video/*', 116 - parseAs: 'blob', 117 - }, 118 - { 119 - content: 'text/*', 120 - parseAs: 'text', 121 - }, 122 - { 123 - content: 'unsupported', 124 - parseAs: undefined, 125 - }, 126 - ]; 127 - 128 - it.each(scenarios)( 129 - 'detects $content as $parseAs', 130 - async ({ content, parseAs }) => { 131 - expect(getParseAs(content)).toEqual(parseAs); 132 - }, 133 - ); 134 - }); 135 - 136 56 describe('setAuthParams', () => { 137 - it('sets access token in headers', async () => { 138 - const accessToken = vi.fn().mockReturnValue('foo'); 139 - const apiKey = vi.fn().mockReturnValue('bar'); 57 + it('sets bearer token in headers', async () => { 58 + const auth = vi.fn().mockReturnValue('foo'); 140 59 const headers = new Headers(); 141 60 const query: Record<any, unknown> = {}; 142 61 await setAuthParams({ 143 - accessToken, 144 - apiKey, 62 + auth, 145 63 headers, 146 64 query, 147 65 security: [ 148 66 { 149 - fn: 'accessToken', 150 - in: 'header', 151 67 name: 'baz', 68 + scheme: 'bearer', 69 + type: 'http', 152 70 }, 153 71 ], 154 72 }); 155 - expect(accessToken).toHaveBeenCalled(); 73 + expect(auth).toHaveBeenCalled(); 156 74 expect(headers.get('baz')).toBe('Bearer foo'); 157 75 expect(Object.keys(query).length).toBe(0); 158 76 }); 159 77 160 78 it('sets access token in query', async () => { 161 - const accessToken = vi.fn().mockReturnValue('foo'); 162 - const apiKey = vi.fn().mockReturnValue('bar'); 79 + const auth = vi.fn().mockReturnValue('foo'); 163 80 const headers = new Headers(); 164 81 const query: Record<any, unknown> = {}; 165 82 await setAuthParams({ 166 - accessToken, 167 - apiKey, 83 + auth, 168 84 headers, 169 85 query, 170 86 security: [ 171 87 { 172 - fn: 'accessToken', 173 88 in: 'query', 174 89 name: 'baz', 90 + scheme: 'bearer', 91 + type: 'http', 175 92 }, 176 93 ], 177 94 }); 178 - expect(accessToken).toHaveBeenCalled(); 95 + expect(auth).toHaveBeenCalled(); 179 96 expect(headers.get('baz')).toBeNull(); 180 97 expect(query.baz).toBe('Bearer foo'); 181 98 }); 182 99 100 + it('sets Authorization header when `in` and `name` are undefined', async () => { 101 + const auth = vi.fn().mockReturnValue('foo'); 102 + const headers = new Headers(); 103 + const query: Record<any, unknown> = {}; 104 + await setAuthParams({ 105 + auth, 106 + headers, 107 + query, 108 + security: [ 109 + { 110 + type: 'http', 111 + }, 112 + ], 113 + }); 114 + expect(auth).toHaveBeenCalled(); 115 + expect(headers.get('Authorization')).toBe('foo'); 116 + expect(query).toEqual({}); 117 + }); 118 + 183 119 it('sets first scheme only', async () => { 184 - const accessToken = vi.fn().mockReturnValue('foo'); 185 - const apiKey = vi.fn().mockReturnValue('bar'); 120 + const auth = vi.fn().mockReturnValue('foo'); 186 121 const headers = new Headers(); 187 122 const query: Record<any, unknown> = {}; 188 123 await setAuthParams({ 189 - accessToken, 190 - apiKey, 124 + auth, 191 125 headers, 192 126 query, 193 127 security: [ 194 128 { 195 - fn: 'accessToken', 196 - in: 'header', 197 129 name: 'baz', 130 + scheme: 'bearer', 131 + type: 'http', 198 132 }, 199 133 { 200 - fn: 'accessToken', 201 134 in: 'query', 202 135 name: 'baz', 136 + scheme: 'bearer', 137 + type: 'http', 203 138 }, 204 139 ], 205 140 }); 206 - expect(accessToken).toHaveBeenCalled(); 141 + expect(auth).toHaveBeenCalled(); 207 142 expect(headers.get('baz')).toBe('Bearer foo'); 208 143 expect(Object.keys(query).length).toBe(0); 209 144 }); 210 145 211 146 it('sets first scheme with token', async () => { 212 - const accessToken = vi.fn().mockReturnValue('foo'); 213 - const apiKey = vi.fn().mockReturnValue(undefined); 147 + const auth = vi.fn().mockImplementation((auth: Auth) => { 148 + if (auth.type === 'apiKey') { 149 + return; 150 + } 151 + return 'foo'; 152 + }); 214 153 const headers = new Headers(); 215 154 const query: Record<any, unknown> = {}; 216 155 await setAuthParams({ 217 - accessToken, 218 - apiKey, 156 + auth, 219 157 headers, 220 158 query, 221 159 security: [ 222 160 { 223 - fn: 'apiKey', 224 - in: 'header', 225 161 name: 'baz', 162 + type: 'apiKey', 226 163 }, 227 164 { 228 - fn: 'accessToken', 229 165 in: 'query', 230 166 name: 'baz', 167 + scheme: 'bearer', 168 + type: 'http', 231 169 }, 232 170 ], 233 171 }); 234 - expect(accessToken).toHaveBeenCalled(); 172 + expect(auth).toHaveBeenCalled(); 235 173 expect(headers.get('baz')).toBeNull(); 236 174 expect(query.baz).toBe('Bearer foo'); 237 175 });
+17 -11
packages/client-nuxt/src/index.ts
··· 38 38 headers: mergeHeaders(_config.headers, options.headers), 39 39 }; 40 40 41 - const { security } = opts; 41 + const { responseTransformer, responseValidator, security } = opts; 42 42 if (security) { 43 43 // auth must happen in interceptors otherwise we'd need to require 44 44 // asyncContext enabled ··· 53 53 }; 54 54 } 55 55 56 + if (responseTransformer || responseValidator) { 57 + opts.onResponse = async ({ options, response }) => { 58 + if (options.responseType && options.responseType !== 'json') { 59 + return; 60 + } 61 + 62 + if (responseValidator) { 63 + await responseValidator(response._data); 64 + } 65 + 66 + if (responseTransformer) { 67 + response._data = await responseTransformer(response._data); 68 + } 69 + }; 70 + } 71 + 56 72 if (opts.body && opts.bodySerializer) { 57 73 opts.body = opts.bodySerializer(opts.body); 58 74 } ··· 65 81 const url = buildUrl(opts); 66 82 67 83 const fetchFn = opts.$fetch; 68 - 69 - // if (parseAs === 'json') { 70 - // if (opts.responseValidator) { 71 - // await opts.responseValidator(data); 72 - // } 73 - 74 - // if (opts.responseTransformer) { 75 - // data = await opts.responseTransformer(data); 76 - // } 77 - // } 78 84 79 85 if (composable === '$fetch') { 80 86 // @ts-expect-error
+2
packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts
··· 491 491 types: isNuxtClient 492 492 ? [ 493 493 { 494 + // default: compiler.ots.string('$fetch'), 494 495 extends: compiler.typeNode('Composable'), 495 496 name: nuxtTypeComposable, 496 497 }, ··· 572 573 types: isNuxtClient 573 574 ? [ 574 575 { 576 + // default: compiler.ots.string('$fetch'), 575 577 extends: compiler.typeNode('Composable'), 576 578 name: nuxtTypeComposable, 577 579 },
+13 -6
packages/openapi-ts/src/plugins/zod/plugin.ts
··· 22 22 export const zodId = 'zod'; 23 23 24 24 // frequently used identifiers 25 + const coerceIdentifier = compiler.identifier({ text: 'coerce' }); 25 26 const defaultIdentifier = compiler.identifier({ text: 'default' }); 26 27 const intersectionIdentifier = compiler.identifier({ text: 'intersection' }); 27 28 const lazyIdentifier = compiler.identifier({ text: 'lazy' }); ··· 263 264 } 264 265 265 266 let numberExpression = compiler.callExpression({ 266 - functionName: compiler.propertyAccessExpression({ 267 - expression: zIdentifier, 268 - name: isBigInt 269 - ? compiler.identifier({ text: 'bigint' }) 270 - : compiler.identifier({ text: 'number' }), 271 - }), 267 + functionName: isBigInt 268 + ? compiler.propertyAccessExpression({ 269 + expression: compiler.propertyAccessExpression({ 270 + expression: zIdentifier, 271 + name: coerceIdentifier, 272 + }), 273 + name: compiler.identifier({ text: 'bigint' }), 274 + }) 275 + : compiler.propertyAccessExpression({ 276 + expression: zIdentifier, 277 + name: compiler.identifier({ text: 'number' }), 278 + }), 272 279 }); 273 280 274 281 if (!isBigInt && schema.type === 'integer') {
+3
pnpm-lock.yaml
··· 264 264 vue-router: 265 265 specifier: 4.5.0 266 266 version: 4.5.0(vue@3.5.13(typescript@5.6.1-rc)) 267 + zod: 268 + specifier: 3.23.8 269 + version: 3.23.8 267 270 devDependencies: 268 271 '@hey-api/openapi-ts': 269 272 specifier: workspace:*