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

chore: update types to support refs

Lubos 7e4b6d84 be2a6e16

+92 -59
+2
examples/openapi-ts-nuxt/.npmrc
··· 1 + engine-strict=true 2 + save-exact=true
+2 -1
examples/openapi-ts-nuxt/components/home.vue
··· 88 88 }); 89 89 90 90 async function handleFetch() { 91 + const petId = ref(BigInt(8)); 91 92 const result = await getPetById({ 92 93 composable: '$fetch', 93 94 path: { 94 - petId: BigInt(8), 95 + petId, 95 96 }, 96 97 }); 97 98 console.log(result);
+3 -3
packages/client-nuxt/package.json
··· 66 66 "typecheck": "vitest --typecheck --watch=false" 67 67 }, 68 68 "peerDependencies": { 69 - "nuxt": ">= 3.0.0 < 4" 69 + "nuxt": ">= 3.0.0 < 4", 70 + "vue": ">= 3.5.13 < 4" 70 71 }, 71 72 "devDependencies": { 72 - "@nuxt/test-utils": "3.15.1", 73 - "nuxt": "3.15.1" 73 + "@nuxt/test-utils": "3.15.1" 74 74 } 75 75 }
+21 -9
packages/client-nuxt/src/index.ts
··· 40 40 41 41 const { responseTransformer, responseValidator, security } = opts; 42 42 if (security) { 43 + const _onRequest = opts.onRequest; 43 44 // auth must happen in interceptors otherwise we'd need to require 44 45 // asyncContext enabled 45 46 // https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext 46 - opts.onRequest = async ({ options }) => { 47 + opts.onRequest = async (context) => { 48 + const { options } = context; 49 + 47 50 await setAuthParams({ 48 51 auth: opts.auth, 49 52 headers: options.headers, 50 53 query: options.query, 51 54 security, 52 55 }); 56 + 57 + if (typeof _onRequest === 'function') { 58 + await _onRequest(context); 59 + } 53 60 }; 54 61 } 55 62 56 63 if (responseTransformer || responseValidator) { 57 - opts.onResponse = async ({ options, response }) => { 58 - if (options.responseType && options.responseType !== 'json') { 59 - return; 60 - } 64 + const _onResponse = opts.onResponse; 65 + opts.onResponse = async (context) => { 66 + const { options, response } = context; 61 67 62 - if (responseValidator) { 63 - await responseValidator(response._data); 68 + if (!options.responseType || options.responseType === 'json') { 69 + if (responseValidator) { 70 + await responseValidator(response._data); 71 + } 72 + 73 + if (responseTransformer) { 74 + response._data = await responseTransformer(response._data); 75 + } 64 76 } 65 77 66 - if (responseTransformer) { 67 - response._data = await responseTransformer(response._data); 78 + if (typeof _onResponse === 'function') { 79 + await _onResponse(context); 68 80 } 69 81 }; 70 82 }
+39 -31
packages/client-nuxt/src/types.ts
··· 6 6 useLazyAsyncData, 7 7 useLazyFetch, 8 8 } from 'nuxt/app'; 9 + import type { Ref } from 'vue'; 9 10 10 11 import type { 11 12 BodySerializer, ··· 14 15 } from './utils'; 15 16 16 17 type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>; 18 + 19 + type WithRefs<TData> = { 20 + [K in keyof TData]: TData[K] extends object 21 + ? WithRefs<TData[K]> | Ref<TData[K]> 22 + : TData[K] | Ref<TData[K]>; 23 + }; 17 24 18 25 export interface Config 19 26 extends Omit< 20 - FetchOptions<unknown>, 21 - 'baseURL' | 'body' | 'headers' | 'method' 22 - > { 27 + FetchOptions<unknown>, 28 + 'baseURL' | 'body' | 'headers' | 'method' | 'query' 29 + >, 30 + WithRefs<Pick<FetchOptions<unknown>, 'query'>> { 23 31 /** 24 32 * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** 25 33 * ··· 106 114 export interface RequestOptions< 107 115 TComposable extends Composable = Composable, 108 116 Url extends string = string, 109 - > extends Config { 117 + > extends Config, 118 + WithRefs<{ 119 + /** 120 + * Any body that you want to add to your request. 121 + * 122 + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} 123 + */ 124 + body?: BodyInit | Record<string, any> | null; 125 + path?: Record<string, unknown>; 126 + query?: FetchOptions<unknown>['query']; 127 + }> { 110 128 asyncDataOptions?: AsyncDataOptions<unknown>; 111 129 /** 112 - * Any body that you want to add to your request. 113 - * 114 - * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} 115 - */ 116 - body?: BodyInit | Record<string, any> | null; 117 - /** 118 130 * You can provide a client instance returned by `createClient()` instead of 119 131 * individual options. This might be also useful if you want to implement a 120 132 * custom client. ··· 122 134 client?: Client; 123 135 composable: TComposable; 124 136 key?: string; 125 - path?: Record<string, unknown>; 126 - query?: FetchOptions<unknown>['query']; 127 137 /** 128 138 * Security mechanism(s) to use for the request. 129 139 */ ··· 164 174 Pick<Required<RequestOptions<TComposable>>, 'method'>, 165 175 ) => RequestResult<TComposable, TData, TError>; 166 176 177 + interface DataShape { 178 + body?: unknown; 179 + headers?: unknown; 180 + path?: Record<string, unknown>; 181 + query?: unknown; 182 + url: string; 183 + } 184 + 185 + export type BuildUrlOptions< 186 + TData extends Omit<DataShape, 'headers'> = Omit<DataShape, 'headers'>, 187 + > = Pick<WithRefs<TData>, 'path' | 'query'> & 188 + Pick<TData, 'url'> & 189 + Pick<Options<'$fetch', TData>, 'baseURL' | 'querySerializer'>; 190 + 167 191 export interface Client { 168 192 /** 169 193 * Returns the final request URL. This method works only with experimental parser. 170 194 */ 171 - buildUrl: < 172 - TData extends { 173 - body?: unknown; 174 - path?: Record<string, unknown>; 175 - query?: FetchOptions<unknown>['query']; 176 - url: string; 177 - }, 178 - >( 179 - options: Pick<TData, 'path' | 'query' | 'url'> & 180 - Pick<Options<'$fetch', TData>, 'baseURL' | 'querySerializer'>, 195 + buildUrl: <TData extends Omit<DataShape, 'headers'>>( 196 + options: BuildUrlOptions<TData>, 181 197 ) => string; 182 198 connect: MethodFn; 183 199 delete: MethodFn; ··· 193 209 trace: MethodFn; 194 210 } 195 211 196 - interface DataShape { 197 - body?: unknown; 198 - headers?: unknown; 199 - path?: unknown; 200 - query?: unknown; 201 - url: string; 202 - } 203 - 204 212 export type Options< 205 213 TComposable extends Composable, 206 214 TData extends DataShape = DataShape, 207 215 > = OmitKeys<RequestOptions<TComposable>, 'body' | 'path' | 'query' | 'url'> & 208 - Omit<TData, 'url'>; 216 + WithRefs<Omit<TData, 'url'>>; 209 217 210 218 export type OptionsLegacyParser<TData = unknown> = TData extends { body?: any } 211 219 ? TData extends { headers?: any }
+18 -12
packages/client-nuxt/src/utils.ts
··· 1 - import type { Auth, Client, Config, RequestOptions } from './types'; 1 + import { toValue } from 'vue'; 2 + 3 + import type { 4 + Auth, 5 + BuildUrlOptions, 6 + Client, 7 + Config, 8 + RequestOptions, 9 + } from './types'; 2 10 3 - interface PathSerializer { 4 - path: Record<string, unknown>; 5 - url: string; 6 - } 11 + type PathSerializer = Pick<Required<BuildUrlOptions>, 'path' | 'url'>; 7 12 8 13 const PATH_PARAM_RE = /\{[^{}]+\}/g; 9 14 ··· 217 222 style = 'matrix'; 218 223 } 219 224 220 - const value = path[name]; 225 + // @ts-expect-error 226 + const value = toValue(toValue(path)[name]); 221 227 222 228 if (value === undefined || value === null) { 223 229 continue; ··· 271 277 }: QuerySerializerOptions = {}) => { 272 278 const querySerializer = (queryParams: T) => { 273 279 let search: string[] = []; 274 - if (queryParams && typeof queryParams === 'object') { 275 - for (const name in queryParams) { 276 - const value = queryParams[name]; 280 + const qParams = toValue(queryParams); 281 + if (qParams && typeof qParams === 'object') { 282 + for (const name in qParams) { 283 + const value = toValue(qParams[name]); 277 284 278 285 if (value === undefined || value === null) { 279 286 continue; ··· 367 374 if (!options.query) { 368 375 options.query = {}; 369 376 } 370 - // TODO: handle refs 371 377 // @ts-expect-error 372 - options.query[name] = token; 378 + toValue(options.query)[name] = token; 373 379 break; 374 380 case 'header': 375 381 default: ··· 401 407 query, 402 408 querySerializer, 403 409 url: _url, 404 - }: Pick<Parameters<Client['buildUrl']>[0], 'path' | 'query' | 'url'> & { 410 + }: Pick<BuildUrlOptions, 'path' | 'query' | 'url'> & { 405 411 baseUrl: string; 406 412 querySerializer: QuerySerializer; 407 413 }) => {
+7 -3
pnpm-lock.yaml
··· 602 602 packages/client-fetch: {} 603 603 604 604 packages/client-nuxt: 605 + dependencies: 606 + nuxt: 607 + specifier: '>= 3.0.0 < 4' 608 + version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0) 609 + vue: 610 + specifier: '>= 3.5.13 < 4' 611 + version: 3.5.13(typescript@5.6.1-rc) 605 612 devDependencies: 606 613 '@nuxt/test-utils': 607 614 specifier: 3.15.1 608 615 version: 3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) 609 - nuxt: 610 - specifier: 3.15.1 611 - version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0) 612 616 613 617 packages/openapi-ts: 614 618 dependencies: