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

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #830 from hey-api/feat/throw-on-error

feat: allow clients to throw on error

authored by

Lubos and committed by
GitHub
c980dbfe 6c0add74

+1097 -904
+5
.changeset/early-papayas-glow.md
··· 1 + --- 2 + '@hey-api/client-fetch': minor 3 + --- 4 + 5 + feat: remove default client export (see migration docs)
+5
.changeset/loud-pillows-design.md
··· 1 + --- 2 + '@hey-api/docs': patch 3 + --- 4 + 5 + docs: split clients documentation into separate pages
+5
.changeset/ninety-bats-design.md
··· 1 + --- 2 + '@hey-api/client-fetch': minor 3 + --- 4 + 5 + feat: add `setConfig()` method
+5
.changeset/odd-geckos-allow.md
··· 1 + --- 2 + '@hey-api/docs': patch 3 + --- 4 + 5 + docs: remove interceptors page in favour of per-client sections
+5
.changeset/plenty-mice-fetch.md
··· 1 + --- 2 + '@hey-api/client-axios': minor 3 + --- 4 + 5 + feat: remove default client export (see migration docs)
+5
.changeset/rich-kangaroos-invent.md
··· 1 + --- 2 + '@hey-api/docs': patch 3 + --- 4 + 5 + docs: add v0.52.0 migration
+5
.changeset/sixty-cats-push.md
··· 1 + --- 2 + '@hey-api/client-axios': minor 3 + --- 4 + 5 + feat: add `setConfig()` method
+5
.changeset/slimy-coins-do.md
··· 1 + --- 2 + '@hey-api/openapi-ts': patch 3 + --- 4 + 5 + fix: generate internal client for services when using standalone package
+19 -2
docs/.vitepress/config/en.ts
··· 16 16 { 17 17 items: [ 18 18 { link: '/openapi-ts/output', text: 'Output' }, 19 - { link: '/openapi-ts/clients', text: 'Clients' }, 20 - { link: '/openapi-ts/interceptors', text: 'Interceptors' }, 19 + { 20 + collapsed: true, 21 + items: [ 22 + { 23 + link: '/openapi-ts/clients/fetch', 24 + text: 'Fetch API', 25 + }, 26 + { 27 + link: '/openapi-ts/clients/axios', 28 + text: 'Axios', 29 + }, 30 + { 31 + link: '/openapi-ts/clients/legacy', 32 + text: 'Legacy', 33 + }, 34 + ], 35 + link: '/openapi-ts/clients', 36 + text: 'Clients', 37 + }, 21 38 { link: '/openapi-ts/transformers', text: 'Transformers' }, 22 39 { link: '/openapi-ts/migrating', text: 'Migrating' }, 23 40 ],
+5 -326
docs/openapi-ts/clients.md
··· 11 11 12 12 We all send HTTP requests in a slightly different way. Hey API doesn't force you to use any specific technology. What we do, however, is support your choice with great clients. All seamlessly integrated with our other features. 13 13 14 - [Next.js](https://nextjs.org/) coming soon. 15 - 16 14 ## Features 17 15 18 16 - seamless integration with `@hey-api/openapi-ts` ··· 22 20 - minimal learning curve thanks to extending the underlying technology 23 21 - support bundling inside the generated output 24 22 25 - ## Fetch API 26 - 27 - ::: warning 28 - Fetch API client is currently in beta. The interface might change before it becomes stable. We encourage you to leave feedback on [GitHub](https://github.com/hey-api/openapi-ts/issues). 29 - ::: 30 - 31 - Start by adding `@hey-api/client-fetch` to your dependencies. 32 - 33 - ::: code-group 34 - 35 - ```sh [npm] 36 - npm install @hey-api/client-fetch 37 - ``` 38 - 39 - ```sh [pnpm] 40 - pnpm add @hey-api/client-fetch 41 - ``` 42 - 43 - ```sh [yarn] 44 - yarn add @hey-api/client-fetch 45 - ``` 46 - 47 - ```sh [bun] 48 - bun add @hey-api/client-fetch 49 - ``` 50 - 51 - ::: 52 - 53 - Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Fetch API client package. 54 - 55 - ```js{2} 56 - export default { 57 - client: '@hey-api/client-fetch', 58 - input: 'path/to/openapi.json', 59 - output: 'src/client', 60 - } 61 - ``` 62 - 63 - You can now run `openapi-ts` to use the new Fetch API client. 🎉 64 - 65 - ### Configuration 66 - 67 - If you're using services, you will want to configure the internal client instance. You can do that with the `createClient()` method. Call it at the beginning of your application. 68 - 69 - ```js 70 - import { createClient } from '@hey-api/client-fetch'; 71 - 72 - createClient({ 73 - baseUrl: 'https://example.com', 74 - }); 75 - ``` 76 - 77 - ### Interceptors 78 - 79 - Another common requirement is request authorization. Interceptors are ideal for adding headers to your requests. 80 - 81 - ```js 82 - import { client } from '@hey-api/client-fetch'; 83 - 84 - client.interceptors.request.use((request, options) => { 85 - request.headers.set('Authorization', 'Bearer <my_token>'); 86 - return request; 87 - }); 88 - ``` 89 - 90 - ### Customization 91 - 92 - Our Fetch client is built as a thin wrapper on top of Fetch API, extending its functionality to work with Hey API. If you're already familiar with Fetch, customizing your client will feel like working directly with Fetch API. You can customize requests in three ways – globally, per client, or per request. 93 - 94 - #### Global 95 - 96 - This is the most common requirement. Our generated services consume an internal Fetch instance, so you will want to configure that. 97 - 98 - ```js 99 - import { createClient } from '@hey-api/client-fetch'; 100 - 101 - createClient({ 102 - baseUrl: 'https://example.com', 103 - }); 104 - ``` 105 - 106 - You can pass any Fetch API configuration option to `createClient()`, and even your own Fetch implementation. 107 - 108 - #### Client 109 - 110 - If you need to create a client instance pointing to a different domain, you can create a non-global client. This client will not be used by default by the generated services. 111 - 112 - ```js 113 - import { createClient } from '@hey-api/client-fetch'; 114 - 115 - const myClient = createClient({ 116 - baseUrl: 'https://example.com', 117 - global: false, // <-- create a non-global client 118 - }); 119 - ``` 120 - 121 - You can then pass this client to any generated service through the `client` option. This will override the default global instance. 122 - 123 - ```js 124 - const response = await getFoo({ 125 - client: myClient, 126 - }); 127 - ``` 23 + ## Available Clients 128 24 129 - #### Request 130 - 131 - Alternatively, you can pass the Fetch API configuration options to each service call directly. This is useful if you don't want to create a separate client for one-off use cases. 132 - 133 - ```js 134 - const response = await getFoo({ 135 - baseUrl: 'https://example.com', // <-- override global configuration 136 - }); 137 - ``` 138 - 139 - ### Example 140 - 141 - You can view a more complete example on this page. 142 - 143 - <button class="buttonLink" @click="(event) => embedProject('hey-api-client-fetch-example')(event)"> 144 - Live demo 145 - </button> 146 - 147 - ## Axios 148 - 149 - ::: warning 150 - Axios client is currently in beta. The interface might change before it becomes stable. We encourage you to leave feedback on [GitHub](https://github.com/hey-api/openapi-ts/issues). 151 - ::: 152 - 153 - Start by adding `@hey-api/client-axios` to your dependencies. 154 - 155 - ::: code-group 156 - 157 - ```sh [npm] 158 - npm install @hey-api/client-axios 159 - ``` 160 - 161 - ```sh [pnpm] 162 - pnpm add @hey-api/client-axios 163 - ``` 164 - 165 - ```sh [yarn] 166 - yarn add @hey-api/client-axios 167 - ``` 168 - 169 - ```sh [bun] 170 - bun add @hey-api/client-axios 171 - ``` 172 - 173 - ::: 174 - 175 - Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Axios client package. 176 - 177 - ```js{2} 178 - export default { 179 - client: '@hey-api/client-axios', 180 - input: 'path/to/openapi.json', 181 - output: 'src/client', 182 - } 183 - ``` 184 - 185 - You can now run `openapi-ts` to use the new Axios client. 🎉 186 - 187 - ### Configuration 188 - 189 - If you're using services, you will want to configure the internal client instance. You can do that with the `createClient()` method. Call it at the beginning of your application. 190 - 191 - ```js 192 - import { createClient } from '@hey-api/client-axios'; 193 - 194 - createClient({ 195 - baseURL: 'https://example.com', 196 - }); 197 - ``` 198 - 199 - ### Interceptors 200 - 201 - Another common requirement is request authorization. Interceptors are ideal for adding headers to your requests. 202 - 203 - ```js 204 - import { client } from '@hey-api/client-axios'; 205 - 206 - client.instance.interceptors.request.use((config) => { 207 - config.headers.set('Authorization', 'Bearer <my_token>'); 208 - return config; 209 - }); 210 - ``` 211 - 212 - ### Customization 213 - 214 - Our Axios client is built as a thin wrapper on top of Axios, extending its functionality to work with Hey API. If you're already familiar with Axios, customizing your client will feel like working directly with Axios. You can customize requests in three ways – globally, per client, or per request. 215 - 216 - #### Global 217 - 218 - This is the most common requirement. Our generated services consume an internal Axios instance, so you will want to configure that. 219 - 220 - ```js 221 - import { createClient } from '@hey-api/client-axios'; 222 - 223 - createClient({ 224 - baseURL: 'https://example.com', 225 - }); 226 - ``` 227 - 228 - You can pass any Axios configuration option to `createClient()`, and even your own Axios implementation. 229 - 230 - #### Client 231 - 232 - If you need to create a client instance pointing to a different domain, you can create a non-global client. This client will not be used by default by the generated services. 233 - 234 - ```js 235 - import { createClient } from '@hey-api/client-axios'; 236 - 237 - const myClient = createClient({ 238 - baseURL: 'https://example.com', 239 - global: false, // <-- create a non-global client 240 - }); 241 - ``` 242 - 243 - You can then pass this client to any generated service through the `client` option. This will override the default global instance. 244 - 245 - ```js 246 - const response = await getFoo({ 247 - client: myClient, 248 - }); 249 - ``` 250 - 251 - #### Request 252 - 253 - Alternatively, you can pass the Axios configuration options to each service call directly. This is useful if you don't want to create a separate client for one-off use cases. 254 - 255 - ```js 256 - const response = await getFoo({ 257 - baseURL: 'https://example.com', // <-- override global configuration 258 - }); 259 - ``` 260 - 261 - ### Example 262 - 263 - You can view a more complete example on this page. 264 - 265 - <button class="buttonLink" @click="(event) => embedProject('hey-api-client-axios-example')(event)"> 266 - Live demo 267 - </button> 268 - 269 - ## Bundling 270 - 271 - Sometimes, you may not want to declare standalone clients as a dependency. This scenario is common if you're using Hey API to generate output that is repackaged and published for other consumers under your own brand. For such cases, our clients support bundling through the `client.bundle` configuration option. 272 - 273 - ```js 274 - export default { 275 - client: { 276 - bundle: true, // [!code ++] 277 - name: '@hey-api/client-fetch', 278 - }, 279 - input: 'path/to/openapi.json', 280 - output: 'src/client', 281 - }; 282 - ``` 283 - 284 - ## Legacy Clients 285 - 286 - Before standalone client packages, clients were generated using `@hey-api/openapi-ts`. In fact, `@hey-api/openapi-ts` still supports generating legacy clients. You can generate them with the `client` config option. 287 - 288 - ::: code-group 289 - 290 - ```js{2} [fetch] 291 - export default { 292 - client: 'fetch', 293 - input: 'path/to/openapi.json', 294 - output: 'src/client', 295 - } 296 - ``` 297 - 298 - ```js{2} [axios] 299 - export default { 300 - client: 'axios', 301 - input: 'path/to/openapi.json', 302 - output: 'src/client', 303 - } 304 - ``` 305 - 306 - ```js{2} [angular] 307 - export default { 308 - client: 'angular', 309 - input: 'path/to/openapi.json', 310 - output: 'src/client', 311 - } 312 - ``` 313 - 314 - ```js{2} [node] 315 - export default { 316 - client: 'node', 317 - input: 'path/to/openapi.json', 318 - output: 'src/client', 319 - } 320 - ``` 321 - 322 - ```js{2} [xhr] 323 - export default { 324 - client: 'xhr', 325 - input: 'path/to/openapi.json', 326 - output: 'src/client', 327 - } 328 - ``` 329 - 330 - ::: 331 - 332 - The following legacy clients are available: 333 - 334 - - [angular](https://angular.io/) (using [RxJS](https://rxjs.dev/)) 335 - - [axios](https://axios-http.com/) 336 - - [fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) 337 - - [node](https://nodejs.org/) (using [node-fetch](https://www.npmjs.com/package/node-fetch)) 338 - - [xhr](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest) 339 - 340 - Please be aware that legacy clients are missing some key features: 341 - 342 - - no typesafe errors 🚫 343 - - no access to the original request and response 🚫 344 - - hard to configure individual requests 👎 345 - - inconsistent interceptors and response APIs 👎 25 + - [Fetch API](/openapi-ts/clients/fetch) 26 + - [Axios](/openapi-ts/clients/axios) 27 + - [Legacy](/openapi-ts/clients/legacy) 28 + - [Next.js](https://nextjs.org/) <span class="soon">Soon</span> 346 29 347 30 If you'd like a standalone package for your client, let us know by [opening an issue](https://github.com/hey-api/openapi-ts/issues). 348 - 349 - ::: tip 350 - You might not need a `node` client. Fetch API is [experimental](https://nodejs.org/docs/latest-v18.x/api/globals.html#fetch) in Node.js v18 and [stable](https://nodejs.org/docs/latest-v21.x/api/globals.html#fetch) in Node.js v21. We recommend upgrading to the latest Node.js version. 351 - ::: 352 31 353 32 <!--@include: ../examples.md-->
+158
docs/openapi-ts/clients/axios.md
··· 1 + --- 2 + title: Axios client 3 + description: Axios client for your stack. Compatible with all our features. 4 + --- 5 + 6 + <script setup> 7 + import { embedProject } from '../../embed' 8 + </script> 9 + 10 + # Axios 11 + 12 + ::: warning 13 + Axios client is currently in beta. The interface might change before it becomes stable. We encourage you to leave feedback on [GitHub](https://github.com/hey-api/openapi-ts/issues). 14 + ::: 15 + 16 + Plug and play Axios wrapper for `@hey-api/openapi-ts` generator. 17 + 18 + <button class="buttonLink" @click="(event) => embedProject('hey-api-client-axios-example')(event)"> 19 + Live demo 20 + </button> 21 + 22 + ## Installation 23 + 24 + Start by adding `@hey-api/client-axios` to your dependencies. 25 + 26 + ::: code-group 27 + 28 + ```sh [npm] 29 + npm install @hey-api/client-axios 30 + ``` 31 + 32 + ```sh [pnpm] 33 + pnpm add @hey-api/client-axios 34 + ``` 35 + 36 + ```sh [yarn] 37 + yarn add @hey-api/client-axios 38 + ``` 39 + 40 + ```sh [bun] 41 + bun add @hey-api/client-axios 42 + ``` 43 + 44 + ::: 45 + 46 + Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Axios client package. 47 + 48 + ```js{2} 49 + export default { 50 + client: '@hey-api/client-axios', 51 + input: 'path/to/openapi.json', 52 + output: 'src/client', 53 + } 54 + ``` 55 + 56 + You can now run `openapi-ts` to use the new Axios client. 🎉 57 + 58 + ## Configuration 59 + 60 + If you're using services, you will want to configure the internal client instance. You can do that with the `setConfig()` method. Call it at the beginning of your application. 61 + 62 + ```js 63 + import { client } from 'client/services.gen'; 64 + 65 + client.setConfig({ 66 + baseURL: 'https://example.com', 67 + }); 68 + ``` 69 + 70 + If you aren't using services, you can create your own client instance. 71 + 72 + ```js 73 + import { createClient } from '@hey-api/client-axios'; 74 + 75 + const client = createClient({ 76 + baseURL: 'https://example.com', 77 + }); 78 + ``` 79 + 80 + ## Interceptors 81 + 82 + Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to the rest of your application. Axios provides interceptors, please refer to their documentation on [interceptors](https://axios-http.com/docs/interceptors). 83 + 84 + We expose the Axios instance through the `instance` field. 85 + 86 + ```js 87 + import { client } from 'client/services.gen'; 88 + 89 + client.instance.interceptors.request.use((config) => { 90 + config.headers.set('Authorization', 'Bearer <my_token>'); 91 + return config; 92 + }); 93 + ``` 94 + 95 + ## Customization 96 + 97 + Our Axios client is built as a thin wrapper on top of Axios, extending its functionality to work with Hey API. If you're already familiar with Axios, customizing your client will feel like working directly with Axios. You can customize requests in three ways – through services, per client, or per request. 98 + 99 + ### Services 100 + 101 + This is the most common requirement. Our generated services consume an internal Axios instance, so you will want to configure that. 102 + 103 + ```js 104 + import { client } from 'client/services.gen'; 105 + 106 + client.setConfig({ 107 + baseURL: 'https://example.com', 108 + }); 109 + ``` 110 + 111 + You can pass any Axios configuration option to `setConfig()`, and even your own Axios implementation. 112 + 113 + ### Client 114 + 115 + If you need to create a client pointing to a different domain, you can create your own client instance. 116 + 117 + ```js 118 + import { createClient } from '@hey-api/client-axios'; 119 + 120 + const myClient = createClient({ 121 + baseURL: 'https://example.com', 122 + }); 123 + ``` 124 + 125 + You can then pass this instance to any generated service through the `client` option. This will override the internal instance. 126 + 127 + ```js 128 + const response = await getFoo({ 129 + client: myClient, 130 + }); 131 + ``` 132 + 133 + ### Request 134 + 135 + Alternatively, you can pass the Axios configuration options to each service call directly. This is useful if you don't want to create a separate client for one-off use cases. 136 + 137 + ```js 138 + const response = await getFoo({ 139 + baseURL: 'https://example.com', // <-- override internal configuration 140 + }); 141 + ``` 142 + 143 + ## Bundling 144 + 145 + Sometimes, you may not want to declare standalone clients as a dependency. This scenario is common if you're using Hey API to generate output that is repackaged and published for other consumers under your own brand. For such cases, our clients support bundling through the `client.bundle` configuration option. 146 + 147 + ```js 148 + export default { 149 + client: { 150 + bundle: true, // [!code ++] 151 + name: '@hey-api/client-axios', 152 + }, 153 + input: 'path/to/openapi.json', 154 + output: 'src/client', 155 + }; 156 + ``` 157 + 158 + <!--@include: ../../examples.md-->
+197
docs/openapi-ts/clients/fetch.md
··· 1 + --- 2 + title: Fetch API client 3 + description: Fetch API client for your stack. Compatible with all our features. 4 + --- 5 + 6 + <script setup> 7 + import { embedProject } from '../../embed' 8 + </script> 9 + 10 + # Fetch API 11 + 12 + ::: warning 13 + Fetch API client is currently in beta. The interface might change before it becomes stable. We encourage you to leave feedback on [GitHub](https://github.com/hey-api/openapi-ts/issues). 14 + ::: 15 + 16 + Plug and play Fetch API wrapper for `@hey-api/openapi-ts` generator. 17 + 18 + <button class="buttonLink" @click="(event) => embedProject('hey-api-client-fetch-example')(event)"> 19 + Live demo 20 + </button> 21 + 22 + ## Installation 23 + 24 + Start by adding `@hey-api/client-fetch` to your dependencies. 25 + 26 + ::: code-group 27 + 28 + ```sh [npm] 29 + npm install @hey-api/client-fetch 30 + ``` 31 + 32 + ```sh [pnpm] 33 + pnpm add @hey-api/client-fetch 34 + ``` 35 + 36 + ```sh [yarn] 37 + yarn add @hey-api/client-fetch 38 + ``` 39 + 40 + ```sh [bun] 41 + bun add @hey-api/client-fetch 42 + ``` 43 + 44 + ::: 45 + 46 + Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Fetch API client package. 47 + 48 + ```js{2} 49 + export default { 50 + client: '@hey-api/client-fetch', 51 + input: 'path/to/openapi.json', 52 + output: 'src/client', 53 + } 54 + ``` 55 + 56 + You can now run `openapi-ts` to use the new Fetch API client. 🎉 57 + 58 + ## Configuration 59 + 60 + If you're using services, you will want to configure the internal client instance. You can do that with the `setConfig()` method. Call it at the beginning of your application. 61 + 62 + ```js 63 + import { client } from 'client/services.gen'; 64 + 65 + client.setConfig({ 66 + baseUrl: 'https://example.com', 67 + }); 68 + ``` 69 + 70 + If you aren't using services, you can create your own client instance. 71 + 72 + ```js 73 + import { createClient } from '@hey-api/client-fetch'; 74 + 75 + const client = createClient({ 76 + baseUrl: 'https://example.com', 77 + }); 78 + ``` 79 + 80 + ## Interceptors 81 + 82 + Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to the rest of your application. Fetch API does not have the interceptor functionality, so we implement our own. Below is an example request interceptor 83 + 84 + ::: code-group 85 + 86 + ```js [use] 87 + import { client } from 'client/services.gen'; 88 + 89 + client.interceptors.request.use((request, options) => { 90 + request.headers.set('Authorization', 'Bearer <my_token>'); 91 + return request; 92 + }); 93 + ``` 94 + 95 + ```js [eject] 96 + import { client } from 'client/services.gen'; 97 + 98 + client.interceptors.request.eject((request, options) => { 99 + request.headers.set('Authorization', 'Bearer <my_token>'); 100 + return request; 101 + }); 102 + ``` 103 + 104 + ::: 105 + 106 + and an example response interceptor 107 + 108 + ::: code-group 109 + 110 + ```js [use] 111 + import { client } from 'client/services.gen'; 112 + 113 + client.interceptors.response.use((response, request, options) => { 114 + trackAnalytics(response); 115 + return response; 116 + }); 117 + ``` 118 + 119 + ```js [eject] 120 + import { client } from 'client/services.gen'; 121 + 122 + client.interceptors.response.eject((response, request, options) => { 123 + trackAnalytics(response); 124 + return response; 125 + }); 126 + ``` 127 + 128 + ::: 129 + 130 + ::: tip 131 + To eject, you must provide a reference to the function that was passed to `use()`. 132 + ::: 133 + 134 + ## Customization 135 + 136 + Our Fetch client is built as a thin wrapper on top of Fetch API, extending its functionality to work with Hey API. If you're already familiar with Fetch, customizing your client will feel like working directly with Fetch API. You can customize requests in three ways – through services, per client, or per request. 137 + 138 + ### Services 139 + 140 + This is the most common requirement. Our generated services consume an internal Fetch instance, so you will want to configure that. 141 + 142 + ```js 143 + import { client } from 'client/services.gen'; 144 + 145 + client.setConfig({ 146 + baseUrl: 'https://example.com', 147 + }); 148 + ``` 149 + 150 + You can pass any Fetch API configuration option to `setConfig()`, and even your own Fetch implementation. 151 + 152 + ### Client 153 + 154 + If you need to create a client pointing to a different domain, you can create your own client instance. 155 + 156 + ```js 157 + import { createClient } from '@hey-api/client-fetch'; 158 + 159 + const myClient = createClient({ 160 + baseUrl: 'https://example.com', 161 + }); 162 + ``` 163 + 164 + You can then pass this instance to any generated service through the `client` option. This will override the internal instance. 165 + 166 + ```js 167 + const response = await getFoo({ 168 + client: myClient, 169 + }); 170 + ``` 171 + 172 + ### Request 173 + 174 + Alternatively, you can pass the Fetch API configuration options to each service call directly. This is useful if you don't want to create a separate client for one-off use cases. 175 + 176 + ```js 177 + const response = await getFoo({ 178 + baseUrl: 'https://example.com', // <-- override internal configuration 179 + }); 180 + ``` 181 + 182 + ## Bundling 183 + 184 + Sometimes, you may not want to declare standalone clients as a dependency. This scenario is common if you're using Hey API to generate output that is repackaged and published for other consumers under your own brand. For such cases, our clients support bundling through the `client.bundle` configuration option. 185 + 186 + ```js 187 + export default { 188 + client: { 189 + bundle: true, // [!code ++] 190 + name: '@hey-api/client-fetch', 191 + }, 192 + input: 'path/to/openapi.json', 193 + output: 'src/client', 194 + }; 195 + ``` 196 + 197 + <!--@include: ../../examples.md-->
+131
docs/openapi-ts/clients/legacy.md
··· 1 + --- 2 + title: Legacy clients 3 + description: Legacy client for your stack. 4 + --- 5 + 6 + <script setup> 7 + import { embedProject } from '../../embed' 8 + </script> 9 + 10 + # Legacy Clients 11 + 12 + Before standalone client packages, clients were generated using `@hey-api/openapi-ts`. In fact, `@hey-api/openapi-ts` still supports generating legacy clients. You can generate them with the `client` config option. 13 + 14 + ::: code-group 15 + 16 + ```js{2} [fetch] 17 + export default { 18 + client: 'fetch', 19 + input: 'path/to/openapi.json', 20 + output: 'src/client', 21 + } 22 + ``` 23 + 24 + ```js{2} [axios] 25 + export default { 26 + client: 'axios', 27 + input: 'path/to/openapi.json', 28 + output: 'src/client', 29 + } 30 + ``` 31 + 32 + ```js{2} [angular] 33 + export default { 34 + client: 'angular', 35 + input: 'path/to/openapi.json', 36 + output: 'src/client', 37 + } 38 + ``` 39 + 40 + ```js{2} [node] 41 + export default { 42 + client: 'node', 43 + input: 'path/to/openapi.json', 44 + output: 'src/client', 45 + } 46 + ``` 47 + 48 + ```js{2} [xhr] 49 + export default { 50 + client: 'xhr', 51 + input: 'path/to/openapi.json', 52 + output: 'src/client', 53 + } 54 + ``` 55 + 56 + ::: 57 + 58 + ## Available Clients 59 + 60 + - [angular](https://angular.io/) (using [RxJS](https://rxjs.dev/)) 61 + - [axios](https://axios-http.com/) 62 + - [fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) 63 + - [node](https://nodejs.org/) (using [node-fetch](https://www.npmjs.com/package/node-fetch)) 64 + - [xhr](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest) 65 + 66 + ## Caveats 67 + 68 + Please be aware that legacy clients are missing some key features: 69 + 70 + - no typesafe errors 🚫 71 + - no access to the original request and response 🚫 72 + - hard to configure individual requests 👎 73 + - inconsistent interceptors and response APIs 👎 74 + 75 + ::: tip 76 + You might not need a `node` client. Fetch API is [experimental](https://nodejs.org/docs/latest-v18.x/api/globals.html#fetch) in Node.js v18 and [stable](https://nodejs.org/docs/latest-v21.x/api/globals.html#fetch) in Node.js v21. We recommend upgrading to the latest Node.js version. 77 + ::: 78 + 79 + ## Interceptors 80 + 81 + Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to the rest of your application. 82 + 83 + Below is an example request interceptor 84 + 85 + ::: code-group 86 + 87 + ```js [use] 88 + OpenAPI.interceptors.request.use((request) => { 89 + doSomethingWithRequest(request); 90 + return request; // <-- must return request 91 + }); 92 + ``` 93 + 94 + ```js [eject] 95 + OpenAPI.interceptors.request.eject((request) => { 96 + doSomethingWithRequest(request); 97 + return request; // <-- must return request 98 + }); 99 + ``` 100 + 101 + ::: 102 + 103 + and an example response interceptor 104 + 105 + ::: code-group 106 + 107 + ```js [use] 108 + OpenAPI.interceptors.response.use(async (response) => { 109 + await doSomethingWithResponse(response); // async 110 + return response; // <-- must return response 111 + }); 112 + ``` 113 + 114 + ```js [eject] 115 + OpenAPI.interceptors.response.eject(async (response) => { 116 + await doSomethingWithResponse(response); // async 117 + return response; // <-- must return response 118 + }); 119 + ``` 120 + 121 + ::: 122 + 123 + ::: tip 124 + To eject, you must provide the same function that was passed to `use()`. 125 + ::: 126 + 127 + ::: warning 128 + Angular client does not currently support request interceptors. 129 + ::: 130 + 131 + <!--@include: ../../examples.md-->
-127
docs/openapi-ts/interceptors.md
··· 1 - --- 2 - title: Interceptors 3 - description: Understanding interceptors. 4 - --- 5 - 6 - # Interceptors 7 - 8 - Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to the rest of your application. If you're not using the standalone client packages, jump to the [legacy clients](#legacy-clients) section. 9 - 10 - ## Fetch API 11 - 12 - Fetch API does not have the interceptor functionality, so we implement our own. Below is an example request interceptor 13 - 14 - ::: code-group 15 - 16 - ```js [use] 17 - import { client } from '@hey-api/client-fetch'; 18 - 19 - client.interceptors.request.use((request, options) => { 20 - request.headers.set('Authorization', 'Bearer <my_token>'); 21 - return request; 22 - }); 23 - ``` 24 - 25 - ```js [eject] 26 - import { client } from '@hey-api/client-fetch'; 27 - 28 - client.interceptors.request.eject((request, options) => { 29 - request.headers.set('Authorization', 'Bearer <my_token>'); 30 - return request; 31 - }); 32 - ``` 33 - 34 - ::: 35 - 36 - and an example response interceptor 37 - 38 - ::: code-group 39 - 40 - ```js [use] 41 - import { client } from '@hey-api/client-fetch'; 42 - 43 - client.interceptors.response.use((response, request, options) => { 44 - trackAnalytics(response); 45 - return response; 46 - }); 47 - ``` 48 - 49 - ```js [eject] 50 - import { client } from '@hey-api/client-fetch'; 51 - 52 - client.interceptors.response.eject((response, request, options) => { 53 - trackAnalytics(response); 54 - return response; 55 - }); 56 - ``` 57 - 58 - ::: 59 - 60 - ::: tip 61 - To eject, you must provide a reference to the function that was passed to `use()`. 62 - ::: 63 - 64 - ## Axios 65 - 66 - If you're using the standalone Axios client, refer to the Axios documentation on [interceptors](https://axios-http.com/docs/interceptors). We expose the Axios instance through the `instance` field. 67 - 68 - ```js 69 - import { client } from '@hey-api/client-axios'; 70 - 71 - client.instance.interceptors.request.use((config) => { 72 - trackAnalytics(config); 73 - return config; 74 - }); 75 - ``` 76 - 77 - ## Legacy Clients 78 - 79 - The following section applies to legacy clients generated by `@hey-api/openapi-ts`. Below is an example request interceptor 80 - 81 - ::: code-group 82 - 83 - ```js [use] 84 - OpenAPI.interceptors.request.use((request) => { 85 - doSomethingWithRequest(request); 86 - return request; // <-- must return request 87 - }); 88 - ``` 89 - 90 - ```js [eject] 91 - OpenAPI.interceptors.request.eject((request) => { 92 - doSomethingWithRequest(request); 93 - return request; // <-- must return request 94 - }); 95 - ``` 96 - 97 - ::: 98 - 99 - and an example response interceptor 100 - 101 - ::: code-group 102 - 103 - ```js [use] 104 - OpenAPI.interceptors.response.use(async (response) => { 105 - await doSomethingWithResponse(response); // async 106 - return response; // <-- must return response 107 - }); 108 - ``` 109 - 110 - ```js [eject] 111 - OpenAPI.interceptors.response.eject(async (response) => { 112 - await doSomethingWithResponse(response); // async 113 - return response; // <-- must return response 114 - }); 115 - ``` 116 - 117 - ::: 118 - 119 - ::: tip 120 - To eject, you must provide the same function that was passed to `use()`. 121 - ::: 122 - 123 - ::: warning 124 - Angular client does not currently support request interceptors. 125 - ::: 126 - 127 - <!--@include: ../examples.md-->
+30
docs/openapi-ts/migrating.md
··· 50 50 51 51 This config option is deprecated and will be removed. 52 52 53 + ## v0.52.0 54 + 55 + ### Removed internal `client` export 56 + 57 + Previously, standalone clients would create a default client which you'd then import and configure. 58 + 59 + ```js 60 + import { client, createClient } from '@hey-api/client-fetch'; 61 + 62 + createClient({ 63 + baseUrl: 'https://example.com', 64 + }); 65 + 66 + console.log(client.getConfig().baseUrl); // <-- 'https://example.com' 67 + ``` 68 + 69 + This client instance was used internally by services unless overridden. Apart from running `createClient()` twice, people were confused about the meaning of `global` configuration option. 70 + 71 + Starting with v0.52.0, standalone clients will not create a default client. Instead, services will define their own client. You can now achieve the same configuration by importing `client` from services and using the new `setConfig()` method. 72 + 73 + ```js 74 + import { client } from 'client/services.gen'; 75 + 76 + client.setConfig({ 77 + baseUrl: 'https://example.com', 78 + }); 79 + 80 + console.log(client.getConfig().baseUrl); // <-- 'https://example.com' 81 + ``` 82 + 53 83 ## v0.51.0 54 84 55 85 ### Required `client` option
-1
examples/openapi-ts-axios/openapi-ts.config.ts
··· 1 1 import { defineConfig } from '@hey-api/openapi-ts'; 2 2 3 3 export default defineConfig({ 4 - base: 'https://petstore3.swagger.io/api/v3', 5 4 client: '@hey-api/client-axios', 6 5 input: 7 6 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml',
+4 -6
examples/openapi-ts-axios/src/App.tsx
··· 18 18 import { useState } from 'react'; 19 19 20 20 import { $Pet } from './client/schemas.gen'; 21 - import { addPet, getPetById, updatePet } from './client/services.gen'; 21 + import { addPet, client, getPetById, updatePet } from './client/services.gen'; 22 22 import type { Pet } from './client/types.gen'; 23 23 24 - createClient({ 24 + // configure internal service client 25 + client.setConfig({ 25 26 // set default base url for requests 26 27 baseURL: 'https://petstore3.swagger.io/api/v3', 27 28 // set default headers for requests 28 29 headers: { 29 - Authorization: 'Bearer <token_from_global_client>', 30 + Authorization: 'Bearer <token_from_service_client>', 30 31 }, 31 32 }); 32 33 33 34 const localClient = createClient({ 34 35 // set default base url for requests made by this client 35 36 baseURL: 'https://petstore3.swagger.io/api/v3', 36 - global: false, 37 37 /** 38 38 * Set default headers only for requests made by this client. This is to 39 39 * demonstrate local clients and their configuration taking precedence over ··· 87 87 ], 88 88 }, 89 89 }); 90 - 91 90 if (error) { 92 91 console.log(error); 93 92 return; 94 93 } 95 - 96 94 setPet(data!); 97 95 setIsRequiredNameError(false); 98 96 };
+7 -1
examples/openapi-ts-axios/src/client/services.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from '@hey-api/client-axios'; 3 + import { 4 + createClient, 5 + createConfig, 6 + type Options, 7 + } from '@hey-api/client-axios'; 4 8 5 9 import type { 6 10 AddPetData, ··· 51 55 UploadFileError, 52 56 UploadFileResponse, 53 57 } from './types.gen'; 58 + 59 + export const client = createClient(createConfig()); 54 60 55 61 /** 56 62 * Add a new pet to the store
-1
examples/openapi-ts-fetch/openapi-ts.config.ts
··· 1 1 import { defineConfig } from '@hey-api/openapi-ts'; 2 2 3 3 export default defineConfig({ 4 - base: 'https://petstore3.swagger.io/api/v3', 5 4 client: '@hey-api/client-fetch', 6 5 input: 7 6 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml',
+5 -5
examples/openapi-ts-fetch/src/App.tsx
··· 18 18 import { useState } from 'react'; 19 19 20 20 import { $Pet } from './client/schemas.gen'; 21 - import { addPet, getPetById, updatePet } from './client/services.gen'; 21 + import { addPet, client, getPetById, updatePet } from './client/services.gen'; 22 22 import type { Pet } from './client/types.gen'; 23 23 24 - createClient({ 24 + // configure internal service client 25 + client.setConfig({ 25 26 // set default base url for requests 26 27 baseUrl: 'https://petstore3.swagger.io/api/v3', 27 28 // set default headers for requests 28 29 headers: { 29 - Authorization: 'Bearer <token_from_global_client>', 30 + Authorization: 'Bearer <token_from_service_client>', 30 31 }, 31 32 }); 32 33 33 34 const localClient = createClient({ 34 35 // set default base url for requests made by this client 35 36 baseUrl: 'https://petstore3.swagger.io/api/v3', 36 - global: false, 37 37 /** 38 38 * Set default headers only for requests made by this client. This is to 39 39 * demonstrate local clients and their configuration taking precedence over 40 - * global configuration. 40 + * internal service client. 41 41 */ 42 42 headers: { 43 43 Authorization: 'Bearer <token_from_local_client>',
+7 -1
examples/openapi-ts-fetch/src/client/services.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from '@hey-api/client-fetch'; 3 + import { 4 + createClient, 5 + createConfig, 6 + type Options, 7 + } from '@hey-api/client-fetch'; 4 8 5 9 import type { 6 10 AddPetData, ··· 51 55 UploadFileError, 52 56 UploadFileResponse, 53 57 } from './types.gen'; 58 + 59 + export const client = createClient(createConfig()); 54 60 55 61 /** 56 62 * Add a new pet to the store
+1 -1
examples/openapi-ts-fetch/src/client/types.gen.ts
··· 258 258 body?: Array<User>; 259 259 }; 260 260 261 - export type CreateUsersWithListInputResponse = User; 261 + export type CreateUsersWithListInputResponse = User | unknown; 262 262 263 263 export type CreateUsersWithListInputError = unknown; 264 264
+27 -27
packages/client-axios/src/index.ts
··· 1 1 import axios, { AxiosError } from 'axios'; 2 2 3 3 import type { Client, Config, RequestOptions } from './types'; 4 - import { createDefaultConfig, getUrl, mergeHeaders } from './utils'; 5 - 6 - let globalConfig = createDefaultConfig(); 4 + import { createConfig, getUrl, mergeConfigs, mergeHeaders } from './utils'; 7 5 8 6 export const createClient = (config: Config): Client => { 9 - const defaultConfig = createDefaultConfig(); 10 - const _config = { ...defaultConfig, ...config }; 7 + let _config = mergeConfigs(createConfig(), config); 11 8 12 - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); 9 + const instance = axios.create(_config); 13 10 14 - if (_config.global) { 15 - globalConfig = { ..._config }; 16 - } 11 + const getConfig = (): Config => ({ ..._config }); 17 12 18 - // @ts-ignore 19 - const getConfig = () => (_config.root ? globalConfig : _config); 20 - 21 - const instance = axios.create(config); 13 + const setConfig = (config: Config): Config => { 14 + _config = mergeConfigs(_config, config); 15 + instance.defaults = { 16 + ...instance.defaults, 17 + ..._config, 18 + // @ts-ignore 19 + headers: mergeHeaders(instance.defaults.headers, _config.headers), 20 + }; 21 + return getConfig(); 22 + }; 22 23 23 24 // @ts-ignore 24 25 const request: Client['request'] = async (options) => { 25 - const config = getConfig(); 26 - 27 26 const opts: RequestOptions = { 28 - ...config, 27 + ..._config, 29 28 ...options, 30 29 // @ts-ignore 31 - headers: mergeHeaders(config.headers, options.headers), 30 + headers: mergeHeaders(_config.headers, options.headers), 32 31 }; 33 32 if (opts.body && opts.bodySerializer) { 34 33 opts.body = opts.bodySerializer(opts.body); ··· 40 39 }); 41 40 42 41 const _axios = opts.axios || instance; 42 + 43 43 try { 44 44 const response = await _axios({ 45 45 ...opts, ··· 47 47 params: opts.query, 48 48 url, 49 49 }); 50 + 50 51 let { data } = response; 52 + 51 53 if (opts.responseType === 'json' && opts.responseTransformer) { 52 54 data = await opts.responseTransformer(data); 53 55 } 56 + 54 57 return { 55 58 ...response, 56 59 data: data ?? {}, 57 60 }; 58 61 } catch (error) { 59 62 const e = error as AxiosError; 60 - return { 61 - ...error, 62 - error: e.response?.data ?? {}, 63 - }; 63 + if (opts.throwOnError) { 64 + throw e; 65 + } 66 + // @ts-ignore 67 + e.error = e.response?.data ?? {}; 68 + return e; 64 69 } 65 70 }; 66 71 ··· 75 80 post: (options) => request({ ...options, method: 'post' }), 76 81 put: (options) => request({ ...options, method: 'put' }), 77 82 request, 83 + setConfig, 78 84 }; 79 85 }; 80 - 81 - export const client = createClient({ 82 - ...globalConfig, 83 - // @ts-ignore 84 - root: true, 85 - });
+3 -2
packages/client-axios/src/node/index.ts
··· 1 - export { client, createClient } from '../'; 2 - export type { Client, Options, RequestResult } from '../types'; 1 + export { createClient } from '../'; 2 + export type { Client, Config, Options, RequestResult } from '../types'; 3 3 export { 4 + createConfig, 4 5 formDataBodySerializer, 5 6 jsonBodySerializer, 6 7 urlSearchParamsBodySerializer,
+11 -9
packages/client-axios/src/types.ts
··· 30 30 */ 31 31 bodySerializer?: BodySerializer; 32 32 /** 33 - * By default, options passed to this call will be applied to the global 34 - * client instance. Set to false to create a local client instance. 35 - * @default true 36 - */ 37 - global?: boolean; 38 - /** 39 33 * An object containing any HTTP headers that you want to pre-populate your 40 34 * `Headers` object with. 41 35 * ··· 74 68 * e.g. convert date ISO strings into native Date objects. 75 69 */ 76 70 responseTransformer?: (data: unknown) => Promise<unknown>; 71 + /** 72 + * Throw an error instead of returning it in the response? 73 + * @default false 74 + */ 75 + throwOnError?: boolean; 77 76 } 78 77 79 - interface RequestOptionsBase extends Omit<Config, 'global'> { 78 + interface RequestOptionsBase extends Config { 80 79 path?: Record<string, unknown>; 81 80 query?: Record<string, unknown>; 82 81 url: string; ··· 88 87 >; 89 88 90 89 type MethodFn = <Data = unknown, Error = unknown>( 91 - options: RequestOptionsBase, 90 + options: Omit<RequestOptionsBase, 'method'>, 92 91 ) => RequestResult<Data, Error>; 92 + 93 93 type RequestFn = <Data = unknown, Error = unknown>( 94 - options: RequestOptionsBase & Pick<Required<RequestOptionsBase>, 'method'>, 94 + options: Omit<RequestOptionsBase, 'method'> & 95 + Pick<Required<RequestOptionsBase>, 'method'>, 95 96 ) => RequestResult<Data, Error>; 96 97 97 98 export interface Client { ··· 105 106 post: MethodFn; 106 107 put: MethodFn; 107 108 request: RequestFn; 109 + setConfig: (config: Config) => Config; 108 110 } 109 111 110 112 export type RequestOptions = RequestOptionsBase &
+8 -2
packages/client-axios/src/utils.ts
··· 270 270 } 271 271 }; 272 272 273 + export const mergeConfigs = (a: Config, b: Config): Config => { 274 + const config = { ...a, ...b }; 275 + config.headers = mergeHeaders(a.headers, b.headers); 276 + return config; 277 + }; 278 + 273 279 export const mergeHeaders = ( 274 280 ...headers: Array<Required<Config>['headers'] | undefined> 275 281 ): Required<Config>['headers'] => { ··· 360 366 }, 361 367 }; 362 368 363 - export const createDefaultConfig = (): Config => ({ 369 + export const createConfig = (override: Config = {}): Config => ({ 364 370 baseURL: '', 365 - global: true, 371 + ...override, 366 372 });
+25 -48
packages/client-fetch/src/index.ts
··· 1 1 import type { Client, Config, RequestOptions } from './types'; 2 2 import { 3 - createDefaultConfig, 3 + createConfig, 4 4 createInterceptors, 5 5 createQuerySerializer, 6 6 getParseAs, 7 7 getUrl, 8 + mergeConfigs, 8 9 mergeHeaders, 9 10 } from './utils'; 10 11 ··· 13 14 headers: ReturnType<typeof mergeHeaders>; 14 15 }; 15 16 16 - let globalConfig = createDefaultConfig(); 17 + export const createClient = (config: Config = {}): Client => { 18 + let _config = mergeConfigs(createConfig(), config); 17 19 18 - const globalInterceptors = createInterceptors< 19 - Request, 20 - Response, 21 - RequestOptions 22 - >(); 20 + const getConfig = (): Config => ({ ..._config }); 23 21 24 - export const createClient = (config: Config): Client => { 25 - const defaultConfig = createDefaultConfig(); 26 - const _config = { ...defaultConfig, ...config }; 22 + const setConfig = (config: Config): Config => { 23 + _config = mergeConfigs(_config, config); 24 + return getConfig(); 25 + }; 27 26 28 - if (_config.baseUrl?.endsWith('/')) { 29 - _config.baseUrl = _config.baseUrl.substring(0, _config.baseUrl.length - 1); 30 - } 31 - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); 32 - 33 - if (_config.global) { 34 - globalConfig = { ..._config }; 35 - } 36 - 37 - // @ts-ignore 38 - const getConfig = () => (_config.root ? globalConfig : _config); 39 - 40 - const interceptors = _config.global 41 - ? globalInterceptors 42 - : createInterceptors<Request, Response, RequestOptions>(); 27 + const interceptors = createInterceptors<Request, Response, RequestOptions>(); 43 28 44 29 // @ts-ignore 45 30 const request: Client['request'] = async (options) => { 46 - const config = getConfig(); 47 - 48 31 const opts: RequestOptions = { 49 - ...config, 32 + ..._config, 50 33 ...options, 51 - headers: mergeHeaders(config.headers, options.headers), 34 + headers: mergeHeaders(_config.headers, options.headers), 52 35 }; 53 36 if (opts.body && opts.bodySerializer) { 54 37 opts.body = opts.bodySerializer(opts.body); ··· 93 76 response, 94 77 }; 95 78 96 - // return empty objects so truthy checks for data/error succeed 97 - if ( 98 - response.status === 204 || 99 - response.headers.get('Content-Length') === '0' 100 - ) { 101 - if (response.ok) { 79 + if (response.ok) { 80 + if ( 81 + response.status === 204 || 82 + response.headers.get('Content-Length') === '0' 83 + ) { 102 84 return { 103 85 data: {}, 104 86 ...result, 105 87 }; 106 88 } 107 - return { 108 - error: {}, 109 - ...result, 110 - }; 111 - } 112 89 113 - if (response.ok) { 114 90 if (opts.parseAs === 'stream') { 115 91 return { 116 92 data: response.body, 117 93 ...result, 118 94 }; 119 95 } 96 + 120 97 const parseAs = 121 98 (opts.parseAs === 'auto' 122 99 ? getParseAs(response.headers.get('Content-Type')) ··· 134 111 } 135 112 136 113 let error = await response.text(); 114 + 115 + if (opts.throwOnError) { 116 + throw new Error(error); 117 + } 118 + 137 119 try { 138 120 error = JSON.parse(error); 139 121 } catch { 140 122 // noop 141 123 } 142 124 return { 143 - error, 125 + error: error || {}, 144 126 ...result, 145 127 }; 146 128 }; ··· 157 139 post: (options) => request({ ...options, method: 'POST' }), 158 140 put: (options) => request({ ...options, method: 'PUT' }), 159 141 request, 142 + setConfig, 160 143 trace: (options) => request({ ...options, method: 'TRACE' }), 161 144 }; 162 145 }; 163 - 164 - export const client = createClient({ 165 - ...globalConfig, 166 - // @ts-ignore 167 - root: true, 168 - });
+2 -1
packages/client-fetch/src/node/index.ts
··· 1 - export { client, createClient } from '../'; 1 + export { createClient } from '../'; 2 2 export type { Client, Config, Options, RequestResult } from '../types'; 3 3 export { 4 + createConfig, 4 5 formDataBodySerializer, 5 6 jsonBodySerializer, 6 7 urlSearchParamsBodySerializer,
+17 -13
packages/client-fetch/src/types.ts
··· 37 37 */ 38 38 fetch?: (request: Request) => ReturnType<typeof fetch>; 39 39 /** 40 - * By default, options passed to this call will be applied to the global 41 - * client instance. Set to false to create a local client instance. 42 - * @default true 43 - */ 44 - global?: boolean; 45 - /** 46 40 * An object containing any HTTP headers that you want to pre-populate your 47 41 * `Headers` object with. 48 42 * ··· 97 91 * e.g. convert date ISO strings into native Date objects. 98 92 */ 99 93 responseTransformer?: (data: unknown) => Promise<unknown>; 94 + /** 95 + * Throw an error instead of returning it in the response? 96 + * @default false 97 + */ 98 + throwOnError?: boolean; 100 99 } 101 100 102 - interface RequestOptionsBase extends Omit<Config, 'global'> { 101 + interface RequestOptionsBase extends Config { 103 102 path?: Record<string, unknown>; 104 103 query?: Record<string, unknown>; 105 104 url: string; ··· 113 112 >; 114 113 115 114 type MethodFn = <Data = unknown, Error = unknown>( 116 - options: RequestOptionsBase, 115 + options: Omit<RequestOptionsBase, 'method'>, 117 116 ) => RequestResult<Data, Error>; 117 + 118 118 type RequestFn = <Data = unknown, Error = unknown>( 119 - options: RequestOptionsBase & Pick<Required<RequestOptionsBase>, 'method'>, 119 + options: Omit<RequestOptionsBase, 'method'> & 120 + Pick<Required<RequestOptionsBase>, 'method'>, 120 121 ) => RequestResult<Data, Error>; 121 122 122 - interface ClientBase<Request = unknown, Response = unknown, Options = unknown> { 123 + export interface Client< 124 + Req = Request, 125 + Res = Response, 126 + Options = RequestOptions, 127 + > { 123 128 connect: MethodFn; 124 129 delete: MethodFn; 125 130 get: MethodFn; 126 131 getConfig: () => Config; 127 132 head: MethodFn; 128 - interceptors: Middleware<Request, Response, Options>; 133 + interceptors: Middleware<Req, Res, Options>; 129 134 options: MethodFn; 130 135 patch: MethodFn; 131 136 post: MethodFn; 132 137 put: MethodFn; 133 138 request: RequestFn; 139 + setConfig: (config: Config) => Config; 134 140 trace: MethodFn; 135 141 } 136 142 ··· 138 144 Config & { 139 145 headers: Headers; 140 146 }; 141 - 142 - export type Client = ClientBase<Request, Response, RequestOptions>; 143 147 144 148 type OptionsBase = Omit<RequestOptionsBase, 'url'> & { 145 149 /**
+11 -2
packages/client-fetch/src/utils.ts
··· 382 382 return url; 383 383 }; 384 384 385 + export const mergeConfigs = (a: Config, b: Config): Config => { 386 + const config = { ...a, ...b }; 387 + if (config.baseUrl?.endsWith('/')) { 388 + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); 389 + } 390 + config.headers = mergeHeaders(a.headers, b.headers); 391 + return config; 392 + }; 393 + 385 394 export const mergeHeaders = ( 386 395 ...headers: Array<Required<Config>['headers'] | undefined> 387 396 ) => { ··· 542 551 'Content-Type': 'application/json', 543 552 }; 544 553 545 - export const createDefaultConfig = (): Config => ({ 554 + export const createConfig = (override: Config = {}): Config => ({ 546 555 ...jsonBodySerializer, 547 556 baseUrl: '', 548 557 fetch: globalThis.fetch, 549 - global: true, 550 558 headers: defaultHeaders, 551 559 parseAs: 'auto', 552 560 querySerializer: defaultQuerySerializer, 561 + ...override, 553 562 });
+5 -6
packages/openapi-ts/src/compiler/classes.ts
··· 1 1 import ts from 'typescript'; 2 2 3 + import { createCallExpression } from './module'; 3 4 import { createTypeNode } from './typedef'; 4 5 import { 5 6 type AccessLevel, ··· 124 125 if (decorator) { 125 126 modifiers = [ 126 127 ts.factory.createDecorator( 127 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 128 - ts.factory.createCallExpression( 129 - ts.factory.createIdentifier(decorator.name), 130 - undefined, 131 - decorator.args 128 + createCallExpression({ 129 + functionName: decorator.name, 130 + parameters: decorator.args 132 131 .map((arg) => toExpression({ value: arg })) 133 132 .filter(isType<ts.Expression>), 134 - ), 133 + }), 135 134 ), 136 135 ...modifiers, 137 136 ];
-22
packages/openapi-ts/src/compiler/function.ts
··· 1 - import ts from 'typescript'; 2 - 3 - export const createCallExpression = ({ 4 - parameters, 5 - functionName, 6 - }: { 7 - functionName: string; 8 - parameters: Array<string>; 9 - }) => { 10 - const functionIdentifier = ts.factory.createIdentifier(functionName); 11 - const argumentsArray = parameters.map((parameter) => 12 - ts.factory.createIdentifier(parameter), 13 - ); 14 - 15 - const callExpression = ts.factory.createCallExpression( 16 - functionIdentifier, 17 - undefined, 18 - argumentsArray, 19 - ); 20 - 21 - return callExpression; 22 - };
+9 -10
packages/openapi-ts/src/compiler/index.ts
··· 5 5 6 6 import * as classes from './classes'; 7 7 import * as convert from './convert'; 8 - import * as functions from './function'; 9 8 import * as module from './module'; 10 9 import * as _return from './return'; 11 10 import * as transform from './transform'; 12 11 import * as typedef from './typedef'; 13 12 import * as types from './types'; 14 - import { stringToTsNodes, tsNodeToString } from './utils'; 13 + import * as utils from './utils'; 15 14 16 15 export type { Property } from './typedef'; 17 16 export type { FunctionParameter } from './types'; ··· 104 103 if (this._imports.length) { 105 104 output = [ 106 105 ...output, 107 - this._imports.map((node) => tsNodeToString({ node })).join('\n'), 106 + this._imports.map((node) => utils.tsNodeToString({ node })).join('\n'), 108 107 ]; 109 108 } 110 109 output = [ ··· 112 111 ...this._items.map((node) => 113 112 typeof node === 'string' 114 113 ? node 115 - : tsNodeToString({ node, unescape: true }), 114 + : utils.tsNodeToString({ node, unescape: true }), 116 115 ), 117 116 ]; 118 117 return output.join(seperator); ··· 138 137 }, 139 138 export: { 140 139 all: module.createExportAllDeclaration, 141 - const: module.createExportConstVariable, 142 140 named: module.createNamedExportDeclarations, 143 - }, 144 - function: { 145 - call: functions.createCallExpression, 146 141 }, 147 142 import: { 148 143 named: module.createNamedImportDeclarations, ··· 177 172 }, 178 173 types: { 179 174 array: types.createArrayType, 175 + call: module.createCallExpression, 176 + const: module.createConstVariable, 180 177 enum: types.createEnumDeclaration, 181 178 function: types.createFunction, 182 179 namespace: types.createNamespaceDeclaration, 183 180 object: types.createObjectType, 184 181 }, 185 182 utils: { 186 - toNode: stringToTsNodes, 187 - toString: tsNodeToString, 183 + isTsNode: utils.isTsNode, 184 + ots: utils.ots, 185 + toNode: utils.stringToTsNodes, 186 + toString: utils.tsNodeToString, 188 187 }, 189 188 };
+36 -5
packages/openapi-ts/src/compiler/module.ts
··· 28 28 29 29 type ImportExportItem = ImportExportItemObject | string; 30 30 31 + export const createCallExpression = ({ 32 + parameters, 33 + functionName, 34 + types, 35 + }: { 36 + functionName: string | ts.PropertyAccessExpression; 37 + parameters: Array<string | ts.Expression>; 38 + types?: ReadonlyArray<ts.TypeNode>; 39 + }) => { 40 + const expression = 41 + typeof functionName === 'string' 42 + ? ts.factory.createIdentifier(functionName) 43 + : functionName; 44 + const argumentsArray = parameters.map((parameter) => 45 + typeof parameter === 'string' 46 + ? ts.factory.createIdentifier(parameter) 47 + : parameter, 48 + ); 49 + const callExpression = ts.factory.createCallExpression( 50 + expression, 51 + types, 52 + argumentsArray, 53 + ); 54 + return callExpression; 55 + }; 56 + 31 57 /** 32 58 * Create a named export declaration. Example: `export { X } from './y'`. 33 59 * @param exports - named imports to export ··· 65 91 }; 66 92 67 93 /** 68 - * Create a const variable export. Optionally, it can use const assertion. 69 - * Example: `export x = {} as const`. 94 + * Create a const variable. Optionally, it can use const assertion or export 95 + * statement. Example: `export x = {} as const`. 70 96 * @param constAssertion use const assertion? 97 + * @param exportConst export created variable? 71 98 * @param expression expression for the variable. 72 99 * @param name name of the variable. 73 100 * @returns ts.VariableStatement 74 101 */ 75 - export const createExportConstVariable = ({ 102 + export const createConstVariable = ({ 76 103 comment, 77 - constAssertion = false, 104 + constAssertion, 78 105 expression, 106 + exportConst, 79 107 name, 80 108 typeName, 81 109 }: { 82 110 comment?: Comments; 83 111 constAssertion?: boolean; 112 + exportConst?: boolean; 84 113 expression: ts.Expression; 85 114 name: string; 86 115 typeName?: string; ··· 98 127 initializer, 99 128 ); 100 129 const statement = ts.factory.createVariableStatement( 101 - [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], 130 + exportConst 131 + ? [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)] 132 + : undefined, 102 133 ts.factory.createVariableDeclarationList([declaration], ts.NodeFlags.Const), 103 134 ); 104 135 if (comment) {
+6 -6
packages/openapi-ts/src/compiler/return.ts
··· 1 1 import ts from 'typescript'; 2 2 3 + import { createCallExpression } from './module'; 3 4 import { isType } from './utils'; 4 5 5 6 /** ··· 27 28 ts.isExpression(arg) ? arg : ts.factory.createIdentifier(arg), 28 29 ) 29 30 .filter(isType<ts.Identifier | ts.Expression>); 30 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 31 - const expression = ts.factory.createCallExpression( 32 - ts.factory.createIdentifier(name), 33 - typeArguments, 34 - argumentsArray, 35 - ); 31 + const expression = createCallExpression({ 32 + functionName: name, 33 + parameters: argumentsArray, 34 + types: typeArguments, 35 + }); 36 36 const statement = createReturnStatement({ expression }); 37 37 return statement; 38 38 };
+21 -30
packages/openapi-ts/src/compiler/transform.ts
··· 1 1 import ts from 'typescript'; 2 2 3 3 import { convertExpressionToStatement } from './convert'; 4 + import { createCallExpression } from './module'; 4 5 import { createReturnStatement } from './return'; 5 6 6 7 export const createSafeAccessExpression = (path: string[]) => ··· 81 82 const thenStatement = ts.factory.createBlock( 82 83 [ 83 84 convertExpressionToStatement({ 84 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 85 - expression: ts.factory.createCallExpression( 86 - ts.factory.createIdentifier(transformerName), 87 - undefined, 88 - [accessExpression], 89 - ), 85 + expression: createCallExpression({ 86 + functionName: transformerName, 87 + parameters: [accessExpression], 88 + }), 90 89 }), 91 90 ], 92 91 true, ··· 113 112 const accessExpression = createAccessExpression(path); 114 113 115 114 const statement = createIfStatement({ 116 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 117 - expression: ts.factory.createCallExpression( 118 - ts.factory.createPropertyAccessExpression( 115 + expression: createCallExpression({ 116 + functionName: ts.factory.createPropertyAccessExpression( 119 117 ts.factory.createIdentifier('Array'), 120 118 ts.factory.createIdentifier('isArray'), 121 119 ), 122 - undefined, 123 - [safeAccessExpression], 124 - ), 120 + parameters: [safeAccessExpression], 121 + }), 125 122 thenStatement: ts.factory.createBlock( 126 123 [ 127 124 convertExpressionToStatement({ ··· 165 162 const accessExpression = createAccessExpression(path); 166 163 167 164 const statement = createIfStatement({ 168 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 169 - expression: ts.factory.createCallExpression( 170 - ts.factory.createPropertyAccessExpression( 165 + expression: createCallExpression({ 166 + functionName: ts.factory.createPropertyAccessExpression( 171 167 ts.factory.createIdentifier('Array'), 172 168 ts.factory.createIdentifier('isArray'), 173 169 ), 174 - undefined, 175 - [safeAccessExpression], 176 - ), 170 + parameters: [safeAccessExpression], 171 + }), 177 172 thenStatement: ts.factory.createBlock( 178 173 [ 179 174 convertExpressionToStatement({ ··· 264 259 ts.factory.createBlock( 265 260 [ 266 261 createIfStatement({ 267 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 268 - expression: ts.factory.createCallExpression( 269 - ts.factory.createPropertyAccessExpression( 262 + expression: createCallExpression({ 263 + functionName: ts.factory.createPropertyAccessExpression( 270 264 ts.factory.createIdentifier('Array'), 271 265 ts.factory.createIdentifier('isArray'), 272 266 ), 273 - undefined, 274 - [ts.factory.createIdentifier('data')], 275 - ), 267 + parameters: ['data'], 268 + }), 276 269 thenStatement: ts.factory.createBlock( 277 270 [ 278 271 convertExpressionToStatement({ 279 - // TODO: refactor to call `createCallExpression()` from 'compiler/function' 280 - expression: ts.factory.createCallExpression( 281 - ts.factory.createPropertyAccessExpression( 272 + expression: createCallExpression({ 273 + functionName: ts.factory.createPropertyAccessExpression( 282 274 ts.factory.createIdentifier('data'), 283 275 ts.factory.createIdentifier('forEach'), 284 276 ), 285 - undefined, 286 - [ts.factory.createIdentifier(transform)], 287 - ), 277 + parameters: [transform], 278 + }), 288 279 }), 289 280 ], 290 281 true,
+17 -9
packages/openapi-ts/src/compiler/types.ts
··· 1 1 import ts from 'typescript'; 2 2 3 3 import { createTypeNode } from './typedef'; 4 - import { addLeadingJSDocComment, type Comments, isType, ots } from './utils'; 4 + import { 5 + addLeadingJSDocComment, 6 + type Comments, 7 + isTsNode, 8 + isType, 9 + ots, 10 + } from './utils'; 5 11 6 12 export type AccessLevel = 'public' | 'protected' | 'private'; 7 13 ··· 230 236 value.value, 231 237 ); 232 238 } else { 233 - let initializer: ts.Expression | undefined = toExpression({ 234 - identifiers: identifiers.includes(value.key) 235 - ? Object.keys(value.value) 236 - : [], 237 - shorthand, 238 - unescape, 239 - value: value.value, 240 - }); 239 + let initializer: ts.Expression | undefined = isTsNode(value.value) 240 + ? value.value 241 + : toExpression({ 242 + identifiers: identifiers.includes(value.key) 243 + ? Object.keys(value.value) 244 + : [], 245 + shorthand, 246 + unescape, 247 + value: value.value, 248 + }); 241 249 if (!initializer) { 242 250 return undefined; 243 251 }
+8
packages/openapi-ts/src/compiler/utils.ts
··· 147 147 }, 148 148 }; 149 149 150 + export const isTsNode = (node: any): node is ts.Expression => 151 + node !== null && 152 + typeof node === 'object' && 153 + typeof node.kind === 'number' && 154 + typeof node.flags === 'number' && 155 + typeof node.pos === 'number' && 156 + typeof node.end === 'number'; 157 + 150 158 export const isType = <T>(value: T | undefined): value is T => 151 159 value !== undefined; 152 160
+2 -1
packages/openapi-ts/src/generate/plugins.ts
··· 66 66 }), 67 67 ], 68 68 }); 69 - const statement = compiler.export.const({ 69 + const statement = compiler.types.const({ 70 + exportConst: true, 70 71 expression, 71 72 name: toQueryOptionsName(operation), 72 73 });
+2 -1
packages/openapi-ts/src/generate/schemas.ts
··· 70 70 const validName = `$${ensureValidTypeScriptJavaScriptIdentifier(name)}`; 71 71 const obj = ensureValidSchemaOutput(schema); 72 72 const expression = compiler.types.object({ obj }); 73 - const statement = compiler.export.const({ 73 + const statement = compiler.types.const({ 74 74 constAssertion: true, 75 + exportConst: true, 75 76 expression, 76 77 name: validName, 77 78 });
+36 -14
packages/openapi-ts/src/generate/services.ts
··· 575 575 onClientImport, 576 576 ), 577 577 }); 578 - const statement = compiler.export.const({ 578 + const statement = compiler.types.const({ 579 579 comment: toOperationComment(operation), 580 + exportConst: true, 580 581 expression, 581 582 name: toOperationName(operation, true), 582 583 }); ··· 666 667 return; 667 668 } 668 669 670 + const isStandalone = isStandaloneClient(config); 671 + 669 672 files.services = new TypeScriptFile({ 670 673 dir: config.output.path, 671 674 name: 'services.ts', 672 675 }); 673 676 677 + // define client first 678 + if (isStandalone) { 679 + const innerFunction = compiler.types.call({ 680 + functionName: 'createConfig', 681 + parameters: [], 682 + }); 683 + const outerFunction = compiler.types.call({ 684 + functionName: 'createClient', 685 + parameters: [innerFunction], 686 + }); 687 + const statement = compiler.types.const({ 688 + exportConst: true, 689 + expression: outerFunction, 690 + name: 'client', 691 + }); 692 + files.services.add(statement); 693 + } 694 + 674 695 let imports: string[] = []; 675 696 let clientImports: string[] = []; 676 697 ··· 684 705 imports = [...imports, imported]; 685 706 }, 686 707 onNode: (node) => { 687 - files.services?.add(node); 708 + files.services.add(node); 688 709 }, 689 710 service, 690 711 }); 691 712 } 692 713 693 714 // Import required packages and core files. 694 - if (isStandaloneClient(config)) { 695 - files.services?.addImport({ 715 + if (isStandalone) { 716 + files.services.addImport({ 696 717 imports: [ 697 - 'client', 718 + 'createClient', 719 + 'createConfig', 698 720 { 699 721 asType: true, 700 722 name: 'Options', ··· 705 727 }); 706 728 } else { 707 729 if (config.client.name === 'angular') { 708 - files.services?.addImport({ 730 + files.services.addImport({ 709 731 imports: 'Injectable', 710 732 module: '@angular/core', 711 733 }); 712 734 713 735 if (!config.name) { 714 - files.services?.addImport({ 736 + files.services.addImport({ 715 737 imports: 'HttpClient', 716 738 module: '@angular/common/http', 717 739 }); 718 740 } 719 741 720 - files.services?.addImport({ 742 + files.services.addImport({ 721 743 imports: { asType: true, name: 'Observable' }, 722 744 module: 'rxjs', 723 745 }); 724 746 } else { 725 - files.services?.addImport({ 747 + files.services.addImport({ 726 748 imports: { asType: true, name: 'CancelablePromise' }, 727 749 module: './core/CancelablePromise', 728 750 }); 729 751 } 730 752 731 753 if (config.services.response === 'response') { 732 - files.services?.addImport({ 754 + files.services.addImport({ 733 755 imports: { asType: true, name: 'ApiResult' }, 734 756 module: './core/ApiResult', 735 757 }); 736 758 } 737 759 738 760 if (config.name) { 739 - files.services?.addImport({ 761 + files.services.addImport({ 740 762 imports: { 741 763 asType: config.client.name !== 'angular', 742 764 name: 'BaseHttpRequest', ··· 744 766 module: './core/BaseHttpRequest', 745 767 }); 746 768 } else { 747 - files.services?.addImport({ 769 + files.services.addImport({ 748 770 imports: 'OpenAPI', 749 771 module: './core/OpenAPI', 750 772 }); 751 - files.services?.addImport({ 773 + files.services.addImport({ 752 774 imports: { alias: '__request', name: 'request' }, 753 775 module: './core/request', 754 776 }); ··· 763 785 name, 764 786 })); 765 787 if (importedTypes.length) { 766 - files.services?.addImport({ 788 + files.services.addImport({ 767 789 imports: importedTypes, 768 790 module: `./${files.types.getName(false)}`, 769 791 });
+3 -2
packages/openapi-ts/src/generate/transformers.ts
··· 161 161 return model.in === 'response' 162 162 ? [ 163 163 compiler.convert.expressionToStatement({ 164 - expression: compiler.function.call({ 164 + expression: compiler.types.call({ 165 165 functionName: nameModelResponseTransformer, 166 166 parameters: [dataVariableName], 167 167 }), ··· 221 221 }), 222 222 ], 223 223 }); 224 - const statement = compiler.export.const({ 224 + const statement = compiler.types.const({ 225 + exportConst: true, 225 226 expression, 226 227 name, 227 228 typeName: name,
+2 -1
packages/openapi-ts/src/generate/types.ts
··· 200 200 obj: properties, 201 201 unescape: true, 202 202 }); 203 - const node = compiler.export.const({ 203 + const node = compiler.types.const({ 204 204 comment, 205 205 constAssertion: true, 206 + exportConst: true, 206 207 expression, 207 208 name, 208 209 });
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-axios'; 3 + import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-axios'; 4 4 import type { ImportData, ImportError, ImportResponse, ApiVversionOdataControllerCountError, ApiVversionOdataControllerCountResponse, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, CallWithNoContentResponseError, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseError, CallWithResponseAndNoContentResponseResponse, DummyAError, DummyAResponse, DummyBError, DummyBResponse, CallWithResponseError, CallWithResponseResponse, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithResponsesError, CallWithResponsesResponse, CollectionFormatData, TypesData, TypesError, TypesResponse, UploadFileData, UploadFileError, UploadFileResponse, FileResponseData, FileResponseError, FileResponseResponse, ComplexTypesData, ComplexTypesError, ComplexTypesResponse, MultipartRequestData, MultipartResponseError, MultipartResponseResponse, ComplexParamsData, ComplexParamsError, ComplexParamsResponse, CallWithResultFromHeaderError, CallWithResultFromHeaderResponse, TestErrorCodeData, TestErrorCodeError, TestErrorCodeResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Error, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const export_ = (options?: Options) => { return (options?.client ?? client).get<void>({ 7 9 ...options,
+3 -2
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle/client.ts.snap
··· 1 - export { client, createClient } from './core/'; 2 - export type { Client, Options, RequestResult } from './core/types'; 1 + export { createClient } from './core/'; 2 + export type { Client, Config, Options, RequestResult } from './core/types'; 3 3 export { 4 + createConfig, 4 5 formDataBodySerializer, 5 6 jsonBodySerializer, 6 7 urlSearchParamsBodySerializer,
+27 -27
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle/core/index.ts.snap
··· 1 1 import axios, { AxiosError } from 'axios'; 2 2 3 3 import type { Client, Config, RequestOptions } from './types'; 4 - import { createDefaultConfig, getUrl, mergeHeaders } from './utils'; 5 - 6 - let globalConfig = createDefaultConfig(); 4 + import { createConfig, getUrl, mergeConfigs, mergeHeaders } from './utils'; 7 5 8 6 export const createClient = (config: Config): Client => { 9 - const defaultConfig = createDefaultConfig(); 10 - const _config = { ...defaultConfig, ...config }; 7 + let _config = mergeConfigs(createConfig(), config); 11 8 12 - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); 9 + const instance = axios.create(_config); 13 10 14 - if (_config.global) { 15 - globalConfig = { ..._config }; 16 - } 11 + const getConfig = (): Config => ({ ..._config }); 17 12 18 - // @ts-ignore 19 - const getConfig = () => (_config.root ? globalConfig : _config); 20 - 21 - const instance = axios.create(config); 13 + const setConfig = (config: Config): Config => { 14 + _config = mergeConfigs(_config, config); 15 + instance.defaults = { 16 + ...instance.defaults, 17 + ..._config, 18 + // @ts-ignore 19 + headers: mergeHeaders(instance.defaults.headers, _config.headers), 20 + }; 21 + return getConfig(); 22 + }; 22 23 23 24 // @ts-ignore 24 25 const request: Client['request'] = async (options) => { 25 - const config = getConfig(); 26 - 27 26 const opts: RequestOptions = { 28 - ...config, 27 + ..._config, 29 28 ...options, 30 29 // @ts-ignore 31 - headers: mergeHeaders(config.headers, options.headers), 30 + headers: mergeHeaders(_config.headers, options.headers), 32 31 }; 33 32 if (opts.body && opts.bodySerializer) { 34 33 opts.body = opts.bodySerializer(opts.body); ··· 40 39 }); 41 40 42 41 const _axios = opts.axios || instance; 42 + 43 43 try { 44 44 const response = await _axios({ 45 45 ...opts, ··· 47 47 params: opts.query, 48 48 url, 49 49 }); 50 + 50 51 let { data } = response; 52 + 51 53 if (opts.responseType === 'json' && opts.responseTransformer) { 52 54 data = await opts.responseTransformer(data); 53 55 } 56 + 54 57 return { 55 58 ...response, 56 59 data: data ?? {}, 57 60 }; 58 61 } catch (error) { 59 62 const e = error as AxiosError; 60 - return { 61 - ...error, 62 - error: e.response?.data ?? {}, 63 - }; 63 + if (opts.throwOnError) { 64 + throw e; 65 + } 66 + // @ts-ignore 67 + e.error = e.response?.data ?? {}; 68 + return e; 64 69 } 65 70 }; 66 71 ··· 75 80 post: (options) => request({ ...options, method: 'post' }), 76 81 put: (options) => request({ ...options, method: 'put' }), 77 82 request, 83 + setConfig, 78 84 }; 79 85 }; 80 - 81 - export const client = createClient({ 82 - ...globalConfig, 83 - // @ts-ignore 84 - root: true, 85 - });
+11 -9
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle/core/types.ts.snap
··· 30 30 */ 31 31 bodySerializer?: BodySerializer; 32 32 /** 33 - * By default, options passed to this call will be applied to the global 34 - * client instance. Set to false to create a local client instance. 35 - * @default true 36 - */ 37 - global?: boolean; 38 - /** 39 33 * An object containing any HTTP headers that you want to pre-populate your 40 34 * `Headers` object with. 41 35 * ··· 74 68 * e.g. convert date ISO strings into native Date objects. 75 69 */ 76 70 responseTransformer?: (data: unknown) => Promise<unknown>; 71 + /** 72 + * Throw an error instead of returning it in the response? 73 + * @default false 74 + */ 75 + throwOnError?: boolean; 77 76 } 78 77 79 - interface RequestOptionsBase extends Omit<Config, 'global'> { 78 + interface RequestOptionsBase extends Config { 80 79 path?: Record<string, unknown>; 81 80 query?: Record<string, unknown>; 82 81 url: string; ··· 88 87 >; 89 88 90 89 type MethodFn = <Data = unknown, Error = unknown>( 91 - options: RequestOptionsBase, 90 + options: Omit<RequestOptionsBase, 'method'>, 92 91 ) => RequestResult<Data, Error>; 92 + 93 93 type RequestFn = <Data = unknown, Error = unknown>( 94 - options: RequestOptionsBase & Pick<Required<RequestOptionsBase>, 'method'>, 94 + options: Omit<RequestOptionsBase, 'method'> & 95 + Pick<Required<RequestOptionsBase>, 'method'>, 95 96 ) => RequestResult<Data, Error>; 96 97 97 98 export interface Client { ··· 105 106 post: MethodFn; 106 107 put: MethodFn; 107 108 request: RequestFn; 109 + setConfig: (config: Config) => Config; 108 110 } 109 111 110 112 export type RequestOptions = RequestOptionsBase &
+8 -2
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle/core/utils.ts.snap
··· 270 270 } 271 271 }; 272 272 273 + export const mergeConfigs = (a: Config, b: Config): Config => { 274 + const config = { ...a, ...b }; 275 + config.headers = mergeHeaders(a.headers, b.headers); 276 + return config; 277 + }; 278 + 273 279 export const mergeHeaders = ( 274 280 ...headers: Array<Required<Config>['headers'] | undefined> 275 281 ): Required<Config>['headers'] => { ··· 360 366 }, 361 367 }; 362 368 363 - export const createDefaultConfig = (): Config => ({ 369 + export const createConfig = (override: Config = {}): Config => ({ 364 370 baseURL: '', 365 - global: true, 371 + ...override, 366 372 });
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from './client'; 3 + import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from './client'; 4 4 import type { ImportData, ImportError, ImportResponse, ApiVversionOdataControllerCountError, ApiVversionOdataControllerCountResponse, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, CallWithNoContentResponseError, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseError, CallWithResponseAndNoContentResponseResponse, DummyAError, DummyAResponse, DummyBError, DummyBResponse, CallWithResponseError, CallWithResponseResponse, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithResponsesError, CallWithResponsesResponse, CollectionFormatData, TypesData, TypesError, TypesResponse, UploadFileData, UploadFileError, UploadFileResponse, FileResponseData, FileResponseError, FileResponseResponse, ComplexTypesData, ComplexTypesError, ComplexTypesResponse, MultipartRequestData, MultipartResponseError, MultipartResponseResponse, ComplexParamsData, ComplexParamsError, ComplexParamsResponse, CallWithResultFromHeaderError, CallWithResultFromHeaderResponse, TestErrorCodeData, TestErrorCodeError, TestErrorCodeResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Error, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const export_ = (options?: Options) => { return (options?.client ?? client).get<void>({ 7 9 ...options,
+3 -2
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle_transform/client.ts.snap
··· 1 - export { client, createClient } from './core/'; 2 - export type { Client, Options, RequestResult } from './core/types'; 1 + export { createClient } from './core/'; 2 + export type { Client, Config, Options, RequestResult } from './core/types'; 3 3 export { 4 + createConfig, 4 5 formDataBodySerializer, 5 6 jsonBodySerializer, 6 7 urlSearchParamsBodySerializer,
+27 -27
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle_transform/core/index.ts.snap
··· 1 1 import axios, { AxiosError } from 'axios'; 2 2 3 3 import type { Client, Config, RequestOptions } from './types'; 4 - import { createDefaultConfig, getUrl, mergeHeaders } from './utils'; 5 - 6 - let globalConfig = createDefaultConfig(); 4 + import { createConfig, getUrl, mergeConfigs, mergeHeaders } from './utils'; 7 5 8 6 export const createClient = (config: Config): Client => { 9 - const defaultConfig = createDefaultConfig(); 10 - const _config = { ...defaultConfig, ...config }; 7 + let _config = mergeConfigs(createConfig(), config); 11 8 12 - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); 9 + const instance = axios.create(_config); 13 10 14 - if (_config.global) { 15 - globalConfig = { ..._config }; 16 - } 11 + const getConfig = (): Config => ({ ..._config }); 17 12 18 - // @ts-ignore 19 - const getConfig = () => (_config.root ? globalConfig : _config); 20 - 21 - const instance = axios.create(config); 13 + const setConfig = (config: Config): Config => { 14 + _config = mergeConfigs(_config, config); 15 + instance.defaults = { 16 + ...instance.defaults, 17 + ..._config, 18 + // @ts-ignore 19 + headers: mergeHeaders(instance.defaults.headers, _config.headers), 20 + }; 21 + return getConfig(); 22 + }; 22 23 23 24 // @ts-ignore 24 25 const request: Client['request'] = async (options) => { 25 - const config = getConfig(); 26 - 27 26 const opts: RequestOptions = { 28 - ...config, 27 + ..._config, 29 28 ...options, 30 29 // @ts-ignore 31 - headers: mergeHeaders(config.headers, options.headers), 30 + headers: mergeHeaders(_config.headers, options.headers), 32 31 }; 33 32 if (opts.body && opts.bodySerializer) { 34 33 opts.body = opts.bodySerializer(opts.body); ··· 40 39 }); 41 40 42 41 const _axios = opts.axios || instance; 42 + 43 43 try { 44 44 const response = await _axios({ 45 45 ...opts, ··· 47 47 params: opts.query, 48 48 url, 49 49 }); 50 + 50 51 let { data } = response; 52 + 51 53 if (opts.responseType === 'json' && opts.responseTransformer) { 52 54 data = await opts.responseTransformer(data); 53 55 } 56 + 54 57 return { 55 58 ...response, 56 59 data: data ?? {}, 57 60 }; 58 61 } catch (error) { 59 62 const e = error as AxiosError; 60 - return { 61 - ...error, 62 - error: e.response?.data ?? {}, 63 - }; 63 + if (opts.throwOnError) { 64 + throw e; 65 + } 66 + // @ts-ignore 67 + e.error = e.response?.data ?? {}; 68 + return e; 64 69 } 65 70 }; 66 71 ··· 75 80 post: (options) => request({ ...options, method: 'post' }), 76 81 put: (options) => request({ ...options, method: 'put' }), 77 82 request, 83 + setConfig, 78 84 }; 79 85 }; 80 - 81 - export const client = createClient({ 82 - ...globalConfig, 83 - // @ts-ignore 84 - root: true, 85 - });
+11 -9
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle_transform/core/types.ts.snap
··· 30 30 */ 31 31 bodySerializer?: BodySerializer; 32 32 /** 33 - * By default, options passed to this call will be applied to the global 34 - * client instance. Set to false to create a local client instance. 35 - * @default true 36 - */ 37 - global?: boolean; 38 - /** 39 33 * An object containing any HTTP headers that you want to pre-populate your 40 34 * `Headers` object with. 41 35 * ··· 74 68 * e.g. convert date ISO strings into native Date objects. 75 69 */ 76 70 responseTransformer?: (data: unknown) => Promise<unknown>; 71 + /** 72 + * Throw an error instead of returning it in the response? 73 + * @default false 74 + */ 75 + throwOnError?: boolean; 77 76 } 78 77 79 - interface RequestOptionsBase extends Omit<Config, 'global'> { 78 + interface RequestOptionsBase extends Config { 80 79 path?: Record<string, unknown>; 81 80 query?: Record<string, unknown>; 82 81 url: string; ··· 88 87 >; 89 88 90 89 type MethodFn = <Data = unknown, Error = unknown>( 91 - options: RequestOptionsBase, 90 + options: Omit<RequestOptionsBase, 'method'>, 92 91 ) => RequestResult<Data, Error>; 92 + 93 93 type RequestFn = <Data = unknown, Error = unknown>( 94 - options: RequestOptionsBase & Pick<Required<RequestOptionsBase>, 'method'>, 94 + options: Omit<RequestOptionsBase, 'method'> & 95 + Pick<Required<RequestOptionsBase>, 'method'>, 95 96 ) => RequestResult<Data, Error>; 96 97 97 98 export interface Client { ··· 105 106 post: MethodFn; 106 107 put: MethodFn; 107 108 request: RequestFn; 109 + setConfig: (config: Config) => Config; 108 110 } 109 111 110 112 export type RequestOptions = RequestOptionsBase &
+8 -2
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle_transform/core/utils.ts.snap
··· 270 270 } 271 271 }; 272 272 273 + export const mergeConfigs = (a: Config, b: Config): Config => { 274 + const config = { ...a, ...b }; 275 + config.headers = mergeHeaders(a.headers, b.headers); 276 + return config; 277 + }; 278 + 273 279 export const mergeHeaders = ( 274 280 ...headers: Array<Required<Config>['headers'] | undefined> 275 281 ): Required<Config>['headers'] => { ··· 360 366 }, 361 367 }; 362 368 363 - export const createDefaultConfig = (): Config => ({ 369 + export const createConfig = (override: Config = {}): Config => ({ 364 370 baseURL: '', 365 - global: true, 371 + ...override, 366 372 });
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_bundle_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from './client'; 3 + import { createClient, createConfig, type Options } from './client'; 4 4 import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 9 ...options,
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-axios_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from '@hey-api/client-axios'; 3 + import { createClient, createConfig, type Options } from '@hey-api/client-axios'; 4 4 import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 9 ...options,
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-fetch'; 3 + import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-fetch'; 4 4 import type { ImportData, ImportError, ImportResponse, ApiVversionOdataControllerCountError, ApiVversionOdataControllerCountResponse, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, CallWithNoContentResponseError, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseError, CallWithResponseAndNoContentResponseResponse, DummyAError, DummyAResponse, DummyBError, DummyBResponse, CallWithResponseError, CallWithResponseResponse, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithResponsesError, CallWithResponsesResponse, CollectionFormatData, TypesData, TypesError, TypesResponse, UploadFileData, UploadFileError, UploadFileResponse, FileResponseData, FileResponseError, FileResponseResponse, ComplexTypesData, ComplexTypesError, ComplexTypesResponse, MultipartRequestData, MultipartResponseError, MultipartResponseResponse, ComplexParamsData, ComplexParamsError, ComplexParamsResponse, CallWithResultFromHeaderError, CallWithResultFromHeaderResponse, TestErrorCodeData, TestErrorCodeError, TestErrorCodeResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Error, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const export_ = (options?: Options) => { return (options?.client ?? client).get<void>({ 7 9 ...options,
+2 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle/client.ts.snap
··· 1 - export { client, createClient } from './core/'; 1 + export { createClient } from './core/'; 2 2 export type { Client, Config, Options, RequestResult } from './core/types'; 3 3 export { 4 + createConfig, 4 5 formDataBodySerializer, 5 6 jsonBodySerializer, 6 7 urlSearchParamsBodySerializer,
+25 -48
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle/core/index.ts.snap
··· 1 1 import type { Client, Config, RequestOptions } from './types'; 2 2 import { 3 - createDefaultConfig, 3 + createConfig, 4 4 createInterceptors, 5 5 createQuerySerializer, 6 6 getParseAs, 7 7 getUrl, 8 + mergeConfigs, 8 9 mergeHeaders, 9 10 } from './utils'; 10 11 ··· 13 14 headers: ReturnType<typeof mergeHeaders>; 14 15 }; 15 16 16 - let globalConfig = createDefaultConfig(); 17 + export const createClient = (config: Config = {}): Client => { 18 + let _config = mergeConfigs(createConfig(), config); 17 19 18 - const globalInterceptors = createInterceptors< 19 - Request, 20 - Response, 21 - RequestOptions 22 - >(); 20 + const getConfig = (): Config => ({ ..._config }); 23 21 24 - export const createClient = (config: Config): Client => { 25 - const defaultConfig = createDefaultConfig(); 26 - const _config = { ...defaultConfig, ...config }; 22 + const setConfig = (config: Config): Config => { 23 + _config = mergeConfigs(_config, config); 24 + return getConfig(); 25 + }; 27 26 28 - if (_config.baseUrl?.endsWith('/')) { 29 - _config.baseUrl = _config.baseUrl.substring(0, _config.baseUrl.length - 1); 30 - } 31 - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); 32 - 33 - if (_config.global) { 34 - globalConfig = { ..._config }; 35 - } 36 - 37 - // @ts-ignore 38 - const getConfig = () => (_config.root ? globalConfig : _config); 39 - 40 - const interceptors = _config.global 41 - ? globalInterceptors 42 - : createInterceptors<Request, Response, RequestOptions>(); 27 + const interceptors = createInterceptors<Request, Response, RequestOptions>(); 43 28 44 29 // @ts-ignore 45 30 const request: Client['request'] = async (options) => { 46 - const config = getConfig(); 47 - 48 31 const opts: RequestOptions = { 49 - ...config, 32 + ..._config, 50 33 ...options, 51 - headers: mergeHeaders(config.headers, options.headers), 34 + headers: mergeHeaders(_config.headers, options.headers), 52 35 }; 53 36 if (opts.body && opts.bodySerializer) { 54 37 opts.body = opts.bodySerializer(opts.body); ··· 93 76 response, 94 77 }; 95 78 96 - // return empty objects so truthy checks for data/error succeed 97 - if ( 98 - response.status === 204 || 99 - response.headers.get('Content-Length') === '0' 100 - ) { 101 - if (response.ok) { 79 + if (response.ok) { 80 + if ( 81 + response.status === 204 || 82 + response.headers.get('Content-Length') === '0' 83 + ) { 102 84 return { 103 85 data: {}, 104 86 ...result, 105 87 }; 106 88 } 107 - return { 108 - error: {}, 109 - ...result, 110 - }; 111 - } 112 89 113 - if (response.ok) { 114 90 if (opts.parseAs === 'stream') { 115 91 return { 116 92 data: response.body, 117 93 ...result, 118 94 }; 119 95 } 96 + 120 97 const parseAs = 121 98 (opts.parseAs === 'auto' 122 99 ? getParseAs(response.headers.get('Content-Type')) ··· 134 111 } 135 112 136 113 let error = await response.text(); 114 + 115 + if (opts.throwOnError) { 116 + throw new Error(error); 117 + } 118 + 137 119 try { 138 120 error = JSON.parse(error); 139 121 } catch { 140 122 // noop 141 123 } 142 124 return { 143 - error, 125 + error: error || {}, 144 126 ...result, 145 127 }; 146 128 }; ··· 157 139 post: (options) => request({ ...options, method: 'POST' }), 158 140 put: (options) => request({ ...options, method: 'PUT' }), 159 141 request, 142 + setConfig, 160 143 trace: (options) => request({ ...options, method: 'TRACE' }), 161 144 }; 162 145 }; 163 - 164 - export const client = createClient({ 165 - ...globalConfig, 166 - // @ts-ignore 167 - root: true, 168 - });
+17 -13
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle/core/types.ts.snap
··· 37 37 */ 38 38 fetch?: (request: Request) => ReturnType<typeof fetch>; 39 39 /** 40 - * By default, options passed to this call will be applied to the global 41 - * client instance. Set to false to create a local client instance. 42 - * @default true 43 - */ 44 - global?: boolean; 45 - /** 46 40 * An object containing any HTTP headers that you want to pre-populate your 47 41 * `Headers` object with. 48 42 * ··· 97 91 * e.g. convert date ISO strings into native Date objects. 98 92 */ 99 93 responseTransformer?: (data: unknown) => Promise<unknown>; 94 + /** 95 + * Throw an error instead of returning it in the response? 96 + * @default false 97 + */ 98 + throwOnError?: boolean; 100 99 } 101 100 102 - interface RequestOptionsBase extends Omit<Config, 'global'> { 101 + interface RequestOptionsBase extends Config { 103 102 path?: Record<string, unknown>; 104 103 query?: Record<string, unknown>; 105 104 url: string; ··· 113 112 >; 114 113 115 114 type MethodFn = <Data = unknown, Error = unknown>( 116 - options: RequestOptionsBase, 115 + options: Omit<RequestOptionsBase, 'method'>, 117 116 ) => RequestResult<Data, Error>; 117 + 118 118 type RequestFn = <Data = unknown, Error = unknown>( 119 - options: RequestOptionsBase & Pick<Required<RequestOptionsBase>, 'method'>, 119 + options: Omit<RequestOptionsBase, 'method'> & 120 + Pick<Required<RequestOptionsBase>, 'method'>, 120 121 ) => RequestResult<Data, Error>; 121 122 122 - interface ClientBase<Request = unknown, Response = unknown, Options = unknown> { 123 + export interface Client< 124 + Req = Request, 125 + Res = Response, 126 + Options = RequestOptions, 127 + > { 123 128 connect: MethodFn; 124 129 delete: MethodFn; 125 130 get: MethodFn; 126 131 getConfig: () => Config; 127 132 head: MethodFn; 128 - interceptors: Middleware<Request, Response, Options>; 133 + interceptors: Middleware<Req, Res, Options>; 129 134 options: MethodFn; 130 135 patch: MethodFn; 131 136 post: MethodFn; 132 137 put: MethodFn; 133 138 request: RequestFn; 139 + setConfig: (config: Config) => Config; 134 140 trace: MethodFn; 135 141 } 136 142 ··· 138 144 Config & { 139 145 headers: Headers; 140 146 }; 141 - 142 - export type Client = ClientBase<Request, Response, RequestOptions>; 143 147 144 148 type OptionsBase = Omit<RequestOptionsBase, 'url'> & { 145 149 /**
+11 -2
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle/core/utils.ts.snap
··· 382 382 return url; 383 383 }; 384 384 385 + export const mergeConfigs = (a: Config, b: Config): Config => { 386 + const config = { ...a, ...b }; 387 + if (config.baseUrl?.endsWith('/')) { 388 + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); 389 + } 390 + config.headers = mergeHeaders(a.headers, b.headers); 391 + return config; 392 + }; 393 + 385 394 export const mergeHeaders = ( 386 395 ...headers: Array<Required<Config>['headers'] | undefined> 387 396 ) => { ··· 542 551 'Content-Type': 'application/json', 543 552 }; 544 553 545 - export const createDefaultConfig = (): Config => ({ 554 + export const createConfig = (override: Config = {}): Config => ({ 546 555 ...jsonBodySerializer, 547 556 baseUrl: '', 548 557 fetch: globalThis.fetch, 549 - global: true, 550 558 headers: defaultHeaders, 551 559 parseAs: 'auto', 552 560 querySerializer: defaultQuerySerializer, 561 + ...override, 553 562 });
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from './client'; 3 + import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from './client'; 4 4 import type { ImportData, ImportError, ImportResponse, ApiVversionOdataControllerCountError, ApiVversionOdataControllerCountResponse, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, CallWithNoContentResponseError, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseError, CallWithResponseAndNoContentResponseResponse, DummyAError, DummyAResponse, DummyBError, DummyBResponse, CallWithResponseError, CallWithResponseResponse, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithResponsesError, CallWithResponsesResponse, CollectionFormatData, TypesData, TypesError, TypesResponse, UploadFileData, UploadFileError, UploadFileResponse, FileResponseData, FileResponseError, FileResponseResponse, ComplexTypesData, ComplexTypesError, ComplexTypesResponse, MultipartRequestData, MultipartResponseError, MultipartResponseResponse, ComplexParamsData, ComplexParamsError, ComplexParamsResponse, CallWithResultFromHeaderError, CallWithResultFromHeaderResponse, TestErrorCodeData, TestErrorCodeError, TestErrorCodeResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Error, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const export_ = (options?: Options) => { return (options?.client ?? client).get<void>({ 7 9 ...options,
+2 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle_transform/client.ts.snap
··· 1 - export { client, createClient } from './core/'; 1 + export { createClient } from './core/'; 2 2 export type { Client, Config, Options, RequestResult } from './core/types'; 3 3 export { 4 + createConfig, 4 5 formDataBodySerializer, 5 6 jsonBodySerializer, 6 7 urlSearchParamsBodySerializer,
+25 -48
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle_transform/core/index.ts.snap
··· 1 1 import type { Client, Config, RequestOptions } from './types'; 2 2 import { 3 - createDefaultConfig, 3 + createConfig, 4 4 createInterceptors, 5 5 createQuerySerializer, 6 6 getParseAs, 7 7 getUrl, 8 + mergeConfigs, 8 9 mergeHeaders, 9 10 } from './utils'; 10 11 ··· 13 14 headers: ReturnType<typeof mergeHeaders>; 14 15 }; 15 16 16 - let globalConfig = createDefaultConfig(); 17 + export const createClient = (config: Config = {}): Client => { 18 + let _config = mergeConfigs(createConfig(), config); 17 19 18 - const globalInterceptors = createInterceptors< 19 - Request, 20 - Response, 21 - RequestOptions 22 - >(); 20 + const getConfig = (): Config => ({ ..._config }); 23 21 24 - export const createClient = (config: Config): Client => { 25 - const defaultConfig = createDefaultConfig(); 26 - const _config = { ...defaultConfig, ...config }; 22 + const setConfig = (config: Config): Config => { 23 + _config = mergeConfigs(_config, config); 24 + return getConfig(); 25 + }; 27 26 28 - if (_config.baseUrl?.endsWith('/')) { 29 - _config.baseUrl = _config.baseUrl.substring(0, _config.baseUrl.length - 1); 30 - } 31 - _config.headers = mergeHeaders(defaultConfig.headers, _config.headers); 32 - 33 - if (_config.global) { 34 - globalConfig = { ..._config }; 35 - } 36 - 37 - // @ts-ignore 38 - const getConfig = () => (_config.root ? globalConfig : _config); 39 - 40 - const interceptors = _config.global 41 - ? globalInterceptors 42 - : createInterceptors<Request, Response, RequestOptions>(); 27 + const interceptors = createInterceptors<Request, Response, RequestOptions>(); 43 28 44 29 // @ts-ignore 45 30 const request: Client['request'] = async (options) => { 46 - const config = getConfig(); 47 - 48 31 const opts: RequestOptions = { 49 - ...config, 32 + ..._config, 50 33 ...options, 51 - headers: mergeHeaders(config.headers, options.headers), 34 + headers: mergeHeaders(_config.headers, options.headers), 52 35 }; 53 36 if (opts.body && opts.bodySerializer) { 54 37 opts.body = opts.bodySerializer(opts.body); ··· 93 76 response, 94 77 }; 95 78 96 - // return empty objects so truthy checks for data/error succeed 97 - if ( 98 - response.status === 204 || 99 - response.headers.get('Content-Length') === '0' 100 - ) { 101 - if (response.ok) { 79 + if (response.ok) { 80 + if ( 81 + response.status === 204 || 82 + response.headers.get('Content-Length') === '0' 83 + ) { 102 84 return { 103 85 data: {}, 104 86 ...result, 105 87 }; 106 88 } 107 - return { 108 - error: {}, 109 - ...result, 110 - }; 111 - } 112 89 113 - if (response.ok) { 114 90 if (opts.parseAs === 'stream') { 115 91 return { 116 92 data: response.body, 117 93 ...result, 118 94 }; 119 95 } 96 + 120 97 const parseAs = 121 98 (opts.parseAs === 'auto' 122 99 ? getParseAs(response.headers.get('Content-Type')) ··· 134 111 } 135 112 136 113 let error = await response.text(); 114 + 115 + if (opts.throwOnError) { 116 + throw new Error(error); 117 + } 118 + 137 119 try { 138 120 error = JSON.parse(error); 139 121 } catch { 140 122 // noop 141 123 } 142 124 return { 143 - error, 125 + error: error || {}, 144 126 ...result, 145 127 }; 146 128 }; ··· 157 139 post: (options) => request({ ...options, method: 'POST' }), 158 140 put: (options) => request({ ...options, method: 'PUT' }), 159 141 request, 142 + setConfig, 160 143 trace: (options) => request({ ...options, method: 'TRACE' }), 161 144 }; 162 145 }; 163 - 164 - export const client = createClient({ 165 - ...globalConfig, 166 - // @ts-ignore 167 - root: true, 168 - });
+17 -13
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle_transform/core/types.ts.snap
··· 37 37 */ 38 38 fetch?: (request: Request) => ReturnType<typeof fetch>; 39 39 /** 40 - * By default, options passed to this call will be applied to the global 41 - * client instance. Set to false to create a local client instance. 42 - * @default true 43 - */ 44 - global?: boolean; 45 - /** 46 40 * An object containing any HTTP headers that you want to pre-populate your 47 41 * `Headers` object with. 48 42 * ··· 97 91 * e.g. convert date ISO strings into native Date objects. 98 92 */ 99 93 responseTransformer?: (data: unknown) => Promise<unknown>; 94 + /** 95 + * Throw an error instead of returning it in the response? 96 + * @default false 97 + */ 98 + throwOnError?: boolean; 100 99 } 101 100 102 - interface RequestOptionsBase extends Omit<Config, 'global'> { 101 + interface RequestOptionsBase extends Config { 103 102 path?: Record<string, unknown>; 104 103 query?: Record<string, unknown>; 105 104 url: string; ··· 113 112 >; 114 113 115 114 type MethodFn = <Data = unknown, Error = unknown>( 116 - options: RequestOptionsBase, 115 + options: Omit<RequestOptionsBase, 'method'>, 117 116 ) => RequestResult<Data, Error>; 117 + 118 118 type RequestFn = <Data = unknown, Error = unknown>( 119 - options: RequestOptionsBase & Pick<Required<RequestOptionsBase>, 'method'>, 119 + options: Omit<RequestOptionsBase, 'method'> & 120 + Pick<Required<RequestOptionsBase>, 'method'>, 120 121 ) => RequestResult<Data, Error>; 121 122 122 - interface ClientBase<Request = unknown, Response = unknown, Options = unknown> { 123 + export interface Client< 124 + Req = Request, 125 + Res = Response, 126 + Options = RequestOptions, 127 + > { 123 128 connect: MethodFn; 124 129 delete: MethodFn; 125 130 get: MethodFn; 126 131 getConfig: () => Config; 127 132 head: MethodFn; 128 - interceptors: Middleware<Request, Response, Options>; 133 + interceptors: Middleware<Req, Res, Options>; 129 134 options: MethodFn; 130 135 patch: MethodFn; 131 136 post: MethodFn; 132 137 put: MethodFn; 133 138 request: RequestFn; 139 + setConfig: (config: Config) => Config; 134 140 trace: MethodFn; 135 141 } 136 142 ··· 138 144 Config & { 139 145 headers: Headers; 140 146 }; 141 - 142 - export type Client = ClientBase<Request, Response, RequestOptions>; 143 147 144 148 type OptionsBase = Omit<RequestOptionsBase, 'url'> & { 145 149 /**
+11 -2
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle_transform/core/utils.ts.snap
··· 382 382 return url; 383 383 }; 384 384 385 + export const mergeConfigs = (a: Config, b: Config): Config => { 386 + const config = { ...a, ...b }; 387 + if (config.baseUrl?.endsWith('/')) { 388 + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); 389 + } 390 + config.headers = mergeHeaders(a.headers, b.headers); 391 + return config; 392 + }; 393 + 385 394 export const mergeHeaders = ( 386 395 ...headers: Array<Required<Config>['headers'] | undefined> 387 396 ) => { ··· 542 551 'Content-Type': 'application/json', 543 552 }; 544 553 545 - export const createDefaultConfig = (): Config => ({ 554 + export const createConfig = (override: Config = {}): Config => ({ 546 555 ...jsonBodySerializer, 547 556 baseUrl: '', 548 557 fetch: globalThis.fetch, 549 - global: true, 550 558 headers: defaultHeaders, 551 559 parseAs: 'auto', 552 560 querySerializer: defaultQuerySerializer, 561 + ...override, 553 562 });
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_bundle_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from './client'; 3 + import { createClient, createConfig, type Options } from './client'; 4 4 import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 9 ...options,
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_plugin_tanstack-react-query/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-fetch'; 3 + import { createClient, createConfig, type Options, formDataBodySerializer, urlSearchParamsBodySerializer } from '@hey-api/client-fetch'; 4 4 import type { ImportData, ImportError, ImportResponse, ApiVversionOdataControllerCountError, ApiVversionOdataControllerCountResponse, DeleteFooData3, CallWithDescriptionsData, DeprecatedCallData, CallWithParametersData, CallWithWeirdParameterNamesData, GetCallWithOptionalParamData, PostCallWithOptionalParamData, PostApiVbyApiVersionRequestBodyData, PostApiVbyApiVersionFormDataData, CallWithDefaultParametersData, CallWithDefaultOptionalParametersData, CallToTestOrderOfParamsData, CallWithNoContentResponseError, CallWithNoContentResponseResponse, CallWithResponseAndNoContentResponseError, CallWithResponseAndNoContentResponseResponse, DummyAError, DummyAResponse, DummyBError, DummyBResponse, CallWithResponseError, CallWithResponseResponse, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithResponsesError, CallWithResponsesResponse, CollectionFormatData, TypesData, TypesError, TypesResponse, UploadFileData, UploadFileError, UploadFileResponse, FileResponseData, FileResponseError, FileResponseResponse, ComplexTypesData, ComplexTypesError, ComplexTypesResponse, MultipartRequestData, MultipartResponseError, MultipartResponseResponse, ComplexParamsData, ComplexParamsError, ComplexParamsResponse, CallWithResultFromHeaderError, CallWithResultFromHeaderResponse, TestErrorCodeData, TestErrorCodeError, TestErrorCodeResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Error, NonAsciiæøåÆøÅöôêÊ字符串Response, PutWithFormUrlEncodedData } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const export_ = (options?: Options) => { return (options?.client ?? client).get<void>({ 7 9 ...options,
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_plugin_tanstack-react-query_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from '@hey-api/client-fetch'; 3 + import { createClient, createConfig, type Options } from '@hey-api/client-fetch'; 4 4 import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 9 ...options,
+3 -1
packages/openapi-ts/test/__snapshots__/test/generated/v3_hey-api_client-fetch_transform/services.gen.ts.snap
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client, type Options } from '@hey-api/client-fetch'; 3 + import { createClient, createConfig, type Options } from '@hey-api/client-fetch'; 4 4 import { type ParentModelWithDatesError, type ParentModelWithDatesResponse, type ModelWithDatesError, type ModelWithDatesResponse, type ModelWithDatesArrayError, type ModelWithDatesArrayResponse, type ArrayOfDatesError, type ArrayOfDatesResponse, type DateError, type DateResponse, type MultipleResponsesError, type MultipleResponsesResponse, ParentModelWithDatesResponseTransformer, ModelWithDatesResponseTransformer, ModelWithDatesArrayResponseTransformer } from './types.gen'; 5 + 6 + export const client = createClient(createConfig()); 5 7 6 8 export const parentModelWithDates = (options?: Options) => { return (options?.client ?? client).post<ParentModelWithDatesResponse, ParentModelWithDatesError>({ 7 9 ...options,
+4 -4
packages/openapi-ts/test/sample.cjs
··· 3 3 const main = async () => { 4 4 /** @type {import('../src/node/index').UserConfig} */ 5 5 const config = { 6 - // client: { 7 - // bundle: true, 8 - // name: '@hey-api/client-fetch', 9 - // }, 6 + client: { 7 + // bundle: true, 8 + name: '@hey-api/client-fetch', 9 + }, 10 10 debug: true, 11 11 // input: './test/spec/v3-transforms.json', 12 12 input: './test/spec/v3.json',