diff --git a/.changeset/flat-brooms-sort.md b/.changeset/flat-brooms-sort.md new file mode 100644 index 000000000..dc007a580 --- /dev/null +++ b/.changeset/flat-brooms-sort.md @@ -0,0 +1,21 @@ +--- +'@hey-api/client-fetch': minor +--- + +**BREAKING**: please update `@hey-api/openapi-ts` to the latest version + +feat: replace accessToken and apiKey functions with auth + +### Added `auth` option + +Client package functions `accessToken` and `apiKey` were replaced with a single `auth` function for fetching auth tokens. If your API supports multiple auth mechanisms, you can use the `auth` argument to return the appropriate token. + +```js +import { client } from 'client/sdk.gen'; + +client.setConfig({ + accessToken: () => '', // [!code --] + apiKey: () => '', // [!code --] + auth: (auth) => '', // [!code ++] +}); +``` diff --git a/.changeset/mean-moles-reflect.md b/.changeset/mean-moles-reflect.md new file mode 100644 index 000000000..ce5ff65f3 --- /dev/null +++ b/.changeset/mean-moles-reflect.md @@ -0,0 +1,6 @@ +--- +'@hey-api/client-axios': minor +'@hey-api/client-fetch': minor +--- + +**BREAKING**: rename exported Security interface to Auth diff --git a/.changeset/old-gorillas-speak.md b/.changeset/old-gorillas-speak.md index f653f02b0..dc232270a 100644 --- a/.changeset/old-gorillas-speak.md +++ b/.changeset/old-gorillas-speak.md @@ -20,12 +20,16 @@ export default { client: '@hey-api/client-fetch', input: 'path/to/openapi.json', output: 'src/client', - watch: true, // [!code ++] + watch: true, }; ``` ### CLI ```sh -npx @hey-api/openapi-ts -i path/to/openapi.json -o src/client -c @hey-api/client-fetch -w +npx @hey-api/openapi-ts \ + -c @hey-api/client-fetch \ + -i path/to/openapi.json \ + -o src/client \ + -w ``` diff --git a/.changeset/tender-bananas-march.md b/.changeset/tender-bananas-march.md new file mode 100644 index 000000000..e375cd41a --- /dev/null +++ b/.changeset/tender-bananas-march.md @@ -0,0 +1,7 @@ +--- +'@hey-api/openapi-ts': minor +--- + +**BREAKING**: please update `@hey-api/client-*` packages to the latest version + +feat: add support for basic http auth diff --git a/.changeset/twenty-walls-compete.md b/.changeset/twenty-walls-compete.md new file mode 100644 index 000000000..c5d0232f9 --- /dev/null +++ b/.changeset/twenty-walls-compete.md @@ -0,0 +1,5 @@ +--- +'@hey-api/client-axios': minor +--- + +**BREAKING**: remove support for passing auth to Axios instance diff --git a/.changeset/wise-poets-flow.md b/.changeset/wise-poets-flow.md index 129ec1830..6e91e9305 100644 --- a/.changeset/wise-poets-flow.md +++ b/.changeset/wise-poets-flow.md @@ -2,4 +2,4 @@ '@hey-api/client-fetch': minor --- -[BREAKING] return raw response body (of type `ReadableStream`) when `Content-Type` response header is not provided and `parseAs` is set to `auto` +**BREAKING**: return raw response body (of type `ReadableStream`) when `Content-Type` response header is not provided and `parseAs` is set to `auto` diff --git a/.changeset/witty-wasps-relate.md b/.changeset/witty-wasps-relate.md new file mode 100644 index 000000000..9e41ad635 --- /dev/null +++ b/.changeset/witty-wasps-relate.md @@ -0,0 +1,23 @@ +--- +'@hey-api/client-axios': minor +--- + +**BREAKING**: please update `@hey-api/openapi-ts` to the latest version + +feat: replace accessToken and apiKey functions with auth + +### Added `auth` option + +Client package functions `accessToken` and `apiKey` were replaced with a single `auth` function for fetching auth tokens. If your API supports multiple auth mechanisms, you can use the `auth` argument to return the appropriate token. + +```js +import { client } from 'client/sdk.gen'; + +client.setConfig({ + accessToken: () => '', // [!code --] + apiKey: () => '', // [!code --] + auth: (auth) => '', // [!code ++] +}); +``` + +Due to conflict with the Axios native `auth` option, we removed support for configuring Axios auth. Please let us know if you require this feature added back. diff --git a/docs/openapi-ts/clients/axios.md b/docs/openapi-ts/clients/axios.md index 458ffa3a5..5be1bb649 100644 --- a/docs/openapi-ts/clients/axios.md +++ b/docs/openapi-ts/clients/axios.md @@ -43,9 +43,11 @@ bun add @hey-api/client-axios ::: -Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Axios client package. +In your [configuration](/openapi-ts/get-started), set `client` to `@hey-api/client-axios` and you'll be ready to use the Axios client. :tada: -```js +::: code-group + +```js [config] export default { client: '@hey-api/client-axios', // [!code ++] input: 'path/to/openapi.json', @@ -53,7 +55,14 @@ export default { }; ``` -You can now run `openapi-ts` to use the new Axios client. 🎉 +```sh [cli] +npx @hey-api/openapi-ts \ + -c @hey-api/client-axios \ # [!code ++] + -i path/to/openapi.json \ + -o src/client +``` + +::: ## Configuration @@ -79,7 +88,7 @@ const client = createClient({ ## Interceptors -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). +Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application. Axios provides interceptors, please refer to their documentation on [interceptors](https://axios-http.com/docs/interceptors). We expose the Axios instance through the `instance` field. @@ -87,7 +96,7 @@ We expose the Axios instance through the `instance` field. import { client } from 'client/sdk.gen'; client.instance.interceptors.request.use((config) => { - config.headers.set('Authorization', 'Bearer '); + // do something return config; }); ``` @@ -98,7 +107,7 @@ The Axios client is built as a thin wrapper on top of Axios, extending its funct ### SDKs -This is the most common requirement. Our generated SDKs consume an internal Axios instance, so you will want to configure that. +This is the most common requirement. The generated SDKs consume an internal client instance, so you will want to configure that. ```js import { client } from 'client/sdk.gen'; @@ -108,7 +117,7 @@ client.setConfig({ }); ``` -You can pass any Axios configuration option to `setConfig()`, and even your own Axios implementation. +You can pass any Axios configuration option to `setConfig()` (except for `auth`), and even your own Axios implementation. ### Client @@ -140,6 +149,34 @@ const response = await getFoo({ }); ``` +## Auth + +::: warning +To use this feature, you must opt in to the [experimental parser](/openapi-ts/configuration#parser). +::: + +The SDKs include auth mechanisms for every endpoint. You will want to configure the `auth` field to pass the right token for each request. The `auth` field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth. + +```js +import { client } from 'client/sdk.gen'; + +client.setConfig({ + auth: () => '', // [!code ++] + baseURL: 'https://example.com', +}); +``` + +If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request. + +```js +import { client } from 'client/sdk.gen'; + +client.instance.interceptors.request.use((config) => { + config.headers.set('Authorization', 'Bearer '); // [!code ++] + return config; +}); +``` + ## Build URL ::: warning diff --git a/docs/openapi-ts/clients/fetch.md b/docs/openapi-ts/clients/fetch.md index e60986c52..dd4ac9390 100644 --- a/docs/openapi-ts/clients/fetch.md +++ b/docs/openapi-ts/clients/fetch.md @@ -43,9 +43,11 @@ bun add @hey-api/client-fetch ::: -Ensure you have already [configured](/openapi-ts/get-started) `@hey-api/openapi-ts`. Update your configuration to use the Fetch API client package. +In your [configuration](/openapi-ts/get-started), set `client` to `@hey-api/client-fetch` and you'll be ready to use the Fetch API client. :tada: -```js +::: code-group + +```js [config] export default { client: '@hey-api/client-fetch', // [!code ++] input: 'path/to/openapi.json', @@ -53,7 +55,14 @@ export default { }; ``` -You can now run `openapi-ts` to use the new Fetch API client. 🎉 +```sh [cli] +npx @hey-api/openapi-ts \ + -c @hey-api/client-fetch \ # [!code ++] + -i path/to/openapi.json \ + -o src/client +``` + +::: ## Configuration @@ -79,15 +88,15 @@ const client = createClient({ ## Interceptors -Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to the rest of your application. They can be added with `use` or removed with `eject`. Fetch API does not have the interceptor functionality, so we implement our own. Below is an example request interceptor +Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application. They can be added with `use` and removed with `eject`. Fetch API does not have the interceptor functionality, so we implement our own. Below is an example request interceptor ::: code-group ```js [use] import { client } from 'client/sdk.gen'; -client.interceptors.request.use((request, options) => { - request.headers.set('Authorization', 'Bearer '); +client.interceptors.request.use((request) => { + // do something return request; }); ``` @@ -95,8 +104,8 @@ client.interceptors.request.use((request, options) => { ```js [eject] import { client } from 'client/sdk.gen'; -client.interceptors.request.eject((request, options) => { - request.headers.set('Authorization', 'Bearer '); +client.interceptors.request.eject((request) => { + // do something return request; }); ``` @@ -110,8 +119,8 @@ and an example response interceptor ```js [use] import { client } from 'client/sdk.gen'; -client.interceptors.response.use((response, request, options) => { - trackAnalytics(response); +client.interceptors.response.use((response) => { + // do something return response; }); ``` @@ -119,8 +128,8 @@ client.interceptors.response.use((response, request, options) => { ```js [eject] import { client } from 'client/sdk.gen'; -client.interceptors.response.eject((response, request, options) => { - trackAnalytics(response); +client.interceptors.response.eject((response) => { + // do something return response; }); ``` @@ -137,7 +146,7 @@ The Fetch client is built as a thin wrapper on top of Fetch API, extending its f ### SDKs -This is the most common requirement. The generated SDKs consume an internal Fetch instance, so you will want to configure that. +This is the most common requirement. The generated SDKs consume an internal client instance, so you will want to configure that. ```js import { client } from 'client/sdk.gen'; @@ -179,6 +188,34 @@ const response = await getFoo({ }); ``` +## Auth + +::: warning +To use this feature, you must opt in to the [experimental parser](/openapi-ts/configuration#parser). +::: + +The SDKs include auth mechanisms for every endpoint. You will want to configure the `auth` field to pass the right token for each request. The `auth` field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth. + +```js +import { client } from 'client/sdk.gen'; + +client.setConfig({ + auth: () => '', // [!code ++] + baseUrl: 'https://example.com', +}); +``` + +If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request. + +```js +import { client } from 'client/sdk.gen'; + +client.interceptors.request.use((request, options) => { + request.headers.set('Authorization', 'Bearer '); // [!code ++] + return request; +}); +``` + ## Build URL ::: warning diff --git a/docs/openapi-ts/clients/legacy.md b/docs/openapi-ts/clients/legacy.md index 295a71c09..812b7c580 100644 --- a/docs/openapi-ts/clients/legacy.md +++ b/docs/openapi-ts/clients/legacy.md @@ -82,7 +82,7 @@ You might not need a `node` client. Fetch API is [experimental](https://nodejs.o ## Interceptors -Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to the rest of your application. +Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application. Below is an example request interceptor diff --git a/docs/openapi-ts/configuration.md b/docs/openapi-ts/configuration.md index fb44f22be..1b78f0074 100644 --- a/docs/openapi-ts/configuration.md +++ b/docs/openapi-ts/configuration.md @@ -110,7 +110,11 @@ export default { ``` ```sh [cli] -npx @hey-api/openapi-ts -i path/to/openapi.json -o src/client -c @hey-api/client-fetch -e +npx @hey-api/openapi-ts \ + -c @hey-api/client-fetch \ + -e \ # [!code ++] + -i path/to/openapi.json \ + -o src/client ``` ::: @@ -274,7 +278,11 @@ export default { ``` ```sh [cli] -npx @hey-api/openapi-ts -i path/to/openapi.json -o src/client -c @hey-api/client-fetch -w +npx @hey-api/openapi-ts \ + -c @hey-api/client-fetch \ + -i path/to/openapi.json \ + -o src/client \ + -w # [!code ++] ``` ::: diff --git a/docs/openapi-ts/get-started.md b/docs/openapi-ts/get-started.md index 8114920f8..34225038d 100644 --- a/docs/openapi-ts/get-started.md +++ b/docs/openapi-ts/get-started.md @@ -33,7 +33,10 @@ Live demo The fastest way to use `@hey-api/openapi-ts` is via npx ```sh -npx @hey-api/openapi-ts -i path/to/openapi.json -o src/client -c @hey-api/client-fetch +npx @hey-api/openapi-ts \ + -c @hey-api/client-fetch \ + -i path/to/openapi.json \ + -o src/client \ ``` Congratulations on creating your first client! 🎉 You can learn more about the generated files on the [Output](/openapi-ts/output) page. diff --git a/docs/openapi-ts/migrating.md b/docs/openapi-ts/migrating.md index 93d87920b..d2bd4c550 100644 --- a/docs/openapi-ts/migrating.md +++ b/docs/openapi-ts/migrating.md @@ -29,10 +29,30 @@ This config option is deprecated and will be removed. ## v0.61.0 +### Added `auth` option + +Client package functions `accessToken` and `apiKey` were replaced with a single `auth` function for fetching auth tokens. If your API supports multiple auth mechanisms, you can use the `auth` argument to return the appropriate token. + +```js +import { client } from 'client/sdk.gen'; + +client.setConfig({ + accessToken: () => '', // [!code --] + apiKey: () => '', // [!code --] + auth: (auth) => '', // [!code ++] +}); +``` + +Due to conflict with the Axios native `auth` option, we removed support for configuring Axios auth. Please let us know if you require this feature added back. + ### Added `watch` option While this is a new feature, supporting it involved replacing the `@apidevtools/json-schema-ref-parser` dependency with our own implementation. Since this was a big change, we're applying caution and marking this as a breaking change. +### Changed `parseAs: 'auto'` behavior + +The Fetch API client will return raw response body as `ReadableStream` when `Content-Type` response header is undefined and `parseAs` is `auto`. + ## v0.60.0 ### Added `sdk.transformer` option diff --git a/docs/openapi-ts/plugins/fastify.md b/docs/openapi-ts/plugins/fastify.md index e9ed32e0d..744b07b0f 100644 --- a/docs/openapi-ts/plugins/fastify.md +++ b/docs/openapi-ts/plugins/fastify.md @@ -27,7 +27,7 @@ Live demo To use this feature, you must opt in to the [experimental parser](/openapi-ts/configuration#parser). ::: -Assuming you have already created a [configuration](/openapi-ts/get-started) file, simply add `fastify` to your plugins and you'll be ready to generate Fastify artifacts. :tada: +In your [configuration](/openapi-ts/get-started), add `fastify` to your plugins and you'll be ready to generate Fastify artifacts. :tada: ```js import { defaultPlugins } from '@hey-api/openapi-ts'; diff --git a/docs/openapi-ts/plugins/tanstack-query.md b/docs/openapi-ts/plugins/tanstack-query.md index 398b9023d..e1824cfbb 100644 --- a/docs/openapi-ts/plugins/tanstack-query.md +++ b/docs/openapi-ts/plugins/tanstack-query.md @@ -28,7 +28,7 @@ Live demo ## Installation -Assuming you have already created a [configuration](/openapi-ts/get-started) file, simply add TanStack Query to your plugins and you'll be ready to generate TanStack Query artifacts. :tada: +In your [configuration](/openapi-ts/get-started), add TanStack Query to your plugins and you'll be ready to generate TanStack Query artifacts. :tada: ::: code-group diff --git a/docs/openapi-ts/plugins/zod.md b/docs/openapi-ts/plugins/zod.md index a54fca970..1a5d9a3fb 100644 --- a/docs/openapi-ts/plugins/zod.md +++ b/docs/openapi-ts/plugins/zod.md @@ -26,7 +26,7 @@ Live demo To use this feature, you must opt in to the [experimental parser](/openapi-ts/configuration#parser). ::: -Assuming you have already created a [configuration](/openapi-ts/get-started) file, simply add `zod` to your plugins and you'll be ready to generate Zod artifacts. :tada: +In your [configuration](/openapi-ts/get-started), add `zod` to your plugins and you'll be ready to generate Zod artifacts. :tada: ```js import { defaultPlugins } from '@hey-api/openapi-ts'; diff --git a/docs/openapi-ts/transformers.md b/docs/openapi-ts/transformers.md index a359ee112..65bbb3b1a 100644 --- a/docs/openapi-ts/transformers.md +++ b/docs/openapi-ts/transformers.md @@ -29,7 +29,7 @@ If your data isn't being transformed as expected, we encourage you to leave feed ## Installation -Assuming you have already created a [configuration](/openapi-ts/get-started) file, simply add `@hey-api/transformers` to your plugins and you'll be ready to generate transformers. :tada: +In your [configuration](/openapi-ts/get-started), add `@hey-api/transformers` to your plugins and you'll be ready to generate transformers. :tada: ```js import { defaultPlugins } from '@hey-api/openapi-ts'; diff --git a/examples/openapi-ts-tanstack-angular-query-experimental/package.json b/examples/openapi-ts-tanstack-angular-query-experimental/package.json index f0391198d..0a58c56d9 100644 --- a/examples/openapi-ts-tanstack-angular-query-experimental/package.json +++ b/examples/openapi-ts-tanstack-angular-query-experimental/package.json @@ -22,7 +22,7 @@ "@angular/platform-browser-dynamic": "^19.0.5", "@angular/router": "^19.0.5", "@hey-api/client-fetch": "workspace:*", - "@tanstack/angular-query-experimental": "5.62.9", + "@tanstack/angular-query-experimental": "5.62.13", "rxjs": "~7.8.0", "tslib": "^2.8.1", "zone.js": "~0.15.0" diff --git a/examples/openapi-ts-tanstack-react-query/package.json b/examples/openapi-ts-tanstack-react-query/package.json index 644dc6aaa..f5da5b9e5 100644 --- a/examples/openapi-ts-tanstack-react-query/package.json +++ b/examples/openapi-ts-tanstack-react-query/package.json @@ -16,8 +16,8 @@ "@radix-ui/react-form": "0.1.1", "@radix-ui/react-icons": "1.3.2", "@radix-ui/themes": "3.1.6", - "@tanstack/react-query": "5.62.11", - "@tanstack/react-query-devtools": "5.62.11", + "@tanstack/react-query": "5.62.15", + "@tanstack/react-query-devtools": "5.62.15", "react": "19.0.0", "react-dom": "19.0.0" }, diff --git a/examples/openapi-ts-tanstack-svelte-query/package.json b/examples/openapi-ts-tanstack-svelte-query/package.json index ca4f5dfc3..ea727b0c5 100644 --- a/examples/openapi-ts-tanstack-svelte-query/package.json +++ b/examples/openapi-ts-tanstack-svelte-query/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@hey-api/client-fetch": "workspace:*", - "@tanstack/svelte-query": "5.62.9" + "@tanstack/svelte-query": "5.62.12" }, "devDependencies": { "@fontsource/fira-mono": "5.0.0", diff --git a/examples/openapi-ts-tanstack-vue-query/package.json b/examples/openapi-ts-tanstack-vue-query/package.json index a04595d74..c05ec747b 100644 --- a/examples/openapi-ts-tanstack-vue-query/package.json +++ b/examples/openapi-ts-tanstack-vue-query/package.json @@ -17,8 +17,8 @@ }, "dependencies": { "@hey-api/client-fetch": "workspace:*", - "@tanstack/vue-query": "5.62.9", - "@tanstack/vue-query-devtools": "5.62.9", + "@tanstack/vue-query": "5.62.12", + "@tanstack/vue-query-devtools": "5.62.12", "pinia": "2.3.0", "vue": "3.5.13", "vue-router": "4.5.0" diff --git a/packages/client-axios/src/__tests__/utils.test.ts b/packages/client-axios/src/__tests__/utils.test.ts index 088338555..d1ab1c93e 100644 --- a/packages/client-axios/src/__tests__/utils.test.ts +++ b/packages/client-axios/src/__tests__/utils.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it, vi } from 'vitest'; +import type { Auth } from '../types'; import { axiosHeadersKeywords, getAuthToken, @@ -8,68 +9,50 @@ import { } from '../utils'; describe('getAuthToken', () => { - it('returns access token', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + it('returns bearer token', async () => { + const auth = vi.fn().mockReturnValue('foo'); const token = await getAuthToken( { - fn: 'accessToken', - in: 'header', - name: 'baz', - }, - { - accessToken, - apiKey, + scheme: 'bearer', + type: 'http', }, + auth, ); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(token).toBe('Bearer foo'); }); - it('returns nothing when accessToken function is undefined', async () => { - const apiKey = vi.fn().mockReturnValue('bar'); + it('returns basic token', async () => { + const auth = vi.fn().mockReturnValue('foo:bar'); const token = await getAuthToken( { - fn: 'accessToken', - in: 'header', - name: 'baz', - }, - { - apiKey, + scheme: 'basic', + type: 'http', }, + auth, ); - expect(token).toBeUndefined(); + expect(auth).toHaveBeenCalled(); + expect(token).toBe(`Basic ${btoa('foo:bar')}`); }); - it('returns API key', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + it('returns raw token', async () => { + const auth = vi.fn().mockReturnValue('foo'); const token = await getAuthToken( { - fn: 'apiKey', - in: 'header', - name: 'baz', - }, - { - accessToken, - apiKey, + type: 'http', }, + auth, ); - expect(apiKey).toHaveBeenCalled(); - expect(token).toBe('bar'); + expect(auth).toHaveBeenCalled(); + expect(token).toBe('foo'); }); - it('returns nothing when apiKey function is undefined', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); + it('returns nothing when auth function is undefined', async () => { const token = await getAuthToken( { - fn: 'apiKey', - in: 'header', - name: 'baz', - }, - { - accessToken, + type: 'http', }, + undefined, ); expect(token).toBeUndefined(); }); @@ -100,105 +83,123 @@ describe('mergeHeaders', () => { }); describe('setAuthParams', () => { - it('sets access token in headers', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + it('sets bearer token in headers', async () => { + const auth = vi.fn().mockReturnValue('foo'); const headers: Record = {}; const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'accessToken', - in: 'header', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(headers.baz).toBe('Bearer foo'); expect(Object.keys(query).length).toBe(0); }); it('sets access token in query', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + const auth = vi.fn().mockReturnValue('foo'); const headers: Record = {}; const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'accessToken', in: 'query', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); - expect(Object.keys(headers).length).toBe(0); + expect(auth).toHaveBeenCalled(); + expect(headers.baz).toBeUndefined(); expect(query.baz).toBe('Bearer foo'); }); + it('sets Authorization header when `in` and `name` are undefined', async () => { + const auth = vi.fn().mockReturnValue('foo'); + const headers: Record = {}; + const query: Record = {}; + await setAuthParams({ + auth, + headers, + query, + security: [ + { + type: 'http', + }, + ], + }); + expect(auth).toHaveBeenCalled(); + expect(headers.Authorization).toBe('foo'); + expect(query).toEqual({}); + }); + it('sets first scheme only', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + const auth = vi.fn().mockReturnValue('foo'); const headers: Record = {}; const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'accessToken', - in: 'header', name: 'baz', + scheme: 'bearer', + type: 'http', }, { - fn: 'accessToken', in: 'query', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(headers.baz).toBe('Bearer foo'); expect(Object.keys(query).length).toBe(0); }); it('sets first scheme with token', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue(undefined); + const auth = vi.fn().mockImplementation((auth: Auth) => { + if (auth.type === 'apiKey') { + return; + } + return 'foo'; + }); const headers: Record = {}; const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'apiKey', - in: 'header', name: 'baz', + type: 'apiKey', }, { - fn: 'accessToken', in: 'query', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); - expect(Object.keys(headers).length).toBe(0); + expect(auth).toHaveBeenCalled(); + expect(headers.baz).toBeUndefined(); expect(query.baz).toBe('Bearer foo'); }); }); diff --git a/packages/client-axios/src/index.ts b/packages/client-axios/src/index.ts index fa2baa5f8..c13193000 100644 --- a/packages/client-axios/src/index.ts +++ b/packages/client-axios/src/index.ts @@ -13,7 +13,9 @@ import { export const createClient = (config: Config): Client => { let _config = mergeConfigs(createConfig(), config); - const instance = axios.create(_config); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { auth, ...configWithoutAuth } = _config; + const instance = axios.create(configWithoutAuth); const getConfig = (): Config => ({ ..._config }); @@ -53,8 +55,10 @@ export const createClient = (config: Config): Client => { try { // assign Axios here for consistency with fetch const _axios = opts.axios; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { auth, ...optsWithoutAuth } = opts; const response = await _axios({ - ...opts, + ...optsWithoutAuth, data: opts.body, headers: opts.headers as RawAxiosRequestHeaders, // let `paramsSerializer()` handle query params if it exists @@ -106,13 +110,13 @@ export const createClient = (config: Config): Client => { }; export type { + Auth, Client, Config, Options, OptionsLegacyParser, RequestOptions, RequestResult, - Security, } from './types'; export { createConfig, diff --git a/packages/client-axios/src/types.ts b/packages/client-axios/src/types.ts index 8db32019c..6912b7f72 100644 --- a/packages/client-axios/src/types.ts +++ b/packages/client-axios/src/types.ts @@ -15,21 +15,14 @@ import type { type OmitKeys = Pick>; export interface Config - extends Omit { + extends Omit { /** * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** * - * Access token or a function returning access token. The resolved token will - * be added to request payload as required. + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. */ - accessToken?: (() => Promise) | string | undefined; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * API key or a function returning API key. The resolved key will be added - * to the request payload as required. - */ - apiKey?: (() => Promise) | string | undefined; + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * Axios implementation. You can use this option to provide a custom * Axios instance. @@ -107,6 +100,15 @@ export interface Config throwOnError?: ThrowOnError; } +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +type AuthToken = string | undefined; + export interface RequestOptions< ThrowOnError extends boolean = boolean, Url extends string = string, @@ -128,7 +130,7 @@ export interface RequestOptions< /** * Security mechanism(s) to use for the request. */ - security?: ReadonlyArray; + security?: ReadonlyArray; url: Url; } @@ -143,12 +145,6 @@ export type RequestResult< | (AxiosError & { data: undefined; error: TError }) >; -export interface Security { - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; -} - type MethodFn = < Data = unknown, TError = unknown, diff --git a/packages/client-axios/src/utils.ts b/packages/client-axios/src/utils.ts index d5dc4e6f7..e8fefad23 100644 --- a/packages/client-axios/src/utils.ts +++ b/packages/client-axios/src/utils.ts @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions, Security } from './types'; +import type { Auth, Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -319,46 +319,54 @@ export const createQuerySerializer = ({ }; export const getAuthToken = async ( - security: Security, - options: Pick, + auth: Auth, + callback: RequestOptions['auth'], ): Promise => { - if (security.fn === 'accessToken') { - const token = - typeof options.accessToken === 'function' - ? await options.accessToken() - : options.accessToken; - return token ? `Bearer ${token}` : undefined; + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; } - if (security.fn === 'apiKey') { - return typeof options.apiKey === 'function' - ? await options.apiKey() - : options.apiKey; + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; } + + return token; }; export const setAuthParams = async ({ security, ...options }: Pick, 'security'> & - Pick & { + Pick & { headers: Record; }) => { - for (const scheme of security) { - const token = await getAuthToken(scheme, options); + for (const auth of security) { + const token = await getAuthToken(auth, options.auth); if (!token) { continue; } - if (scheme.in === 'header') { - options.headers[scheme.name] = token; - } else if (scheme.in === 'query') { - if (!options.query) { - options.query = {}; - } + const name = auth.name ?? 'Authorization'; - options.query[scheme.name] = token; + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'header': + default: + options.headers[name] = token; + break; } return; diff --git a/packages/client-fetch/src/__tests__/utils.test.ts b/packages/client-fetch/src/__tests__/utils.test.ts index 74cd0c986..1f21382ab 100644 --- a/packages/client-fetch/src/__tests__/utils.test.ts +++ b/packages/client-fetch/src/__tests__/utils.test.ts @@ -1,70 +1,53 @@ import { describe, expect, it, vi } from 'vitest'; +import type { Auth } from '../types'; import { getAuthToken, getParseAs, setAuthParams } from '../utils'; describe('getAuthToken', () => { - it('returns access token', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + it('returns bearer token', async () => { + const auth = vi.fn().mockReturnValue('foo'); const token = await getAuthToken( { - fn: 'accessToken', - in: 'header', - name: 'baz', - }, - { - accessToken, - apiKey, + scheme: 'bearer', + type: 'http', }, + auth, ); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(token).toBe('Bearer foo'); }); - it('returns nothing when accessToken function is undefined', async () => { - const apiKey = vi.fn().mockReturnValue('bar'); + it('returns basic token', async () => { + const auth = vi.fn().mockReturnValue('foo:bar'); const token = await getAuthToken( { - fn: 'accessToken', - in: 'header', - name: 'baz', - }, - { - apiKey, + scheme: 'basic', + type: 'http', }, + auth, ); - expect(token).toBeUndefined(); + expect(auth).toHaveBeenCalled(); + expect(token).toBe(`Basic ${btoa('foo:bar')}`); }); - it('returns API key', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + it('returns raw token', async () => { + const auth = vi.fn().mockReturnValue('foo'); const token = await getAuthToken( { - fn: 'apiKey', - in: 'header', - name: 'baz', - }, - { - accessToken, - apiKey, + type: 'http', }, + auth, ); - expect(apiKey).toHaveBeenCalled(); - expect(token).toBe('bar'); + expect(auth).toHaveBeenCalled(); + expect(token).toBe('foo'); }); - it('returns nothing when apiKey function is undefined', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); + it('returns nothing when auth function is undefined', async () => { const token = await getAuthToken( { - fn: 'apiKey', - in: 'header', - name: 'baz', - }, - { - accessToken, + type: 'http', }, + undefined, ); expect(token).toBeUndefined(); }); @@ -134,104 +117,122 @@ describe('getParseAs', () => { }); describe('setAuthParams', () => { - it('sets access token in headers', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + it('sets bearer token in headers', async () => { + const auth = vi.fn().mockReturnValue('foo'); const headers = new Headers(); const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'accessToken', - in: 'header', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(headers.get('baz')).toBe('Bearer foo'); expect(Object.keys(query).length).toBe(0); }); it('sets access token in query', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + const auth = vi.fn().mockReturnValue('foo'); const headers = new Headers(); const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'accessToken', in: 'query', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(headers.get('baz')).toBeNull(); expect(query.baz).toBe('Bearer foo'); }); + it('sets Authorization header when `in` and `name` are undefined', async () => { + const auth = vi.fn().mockReturnValue('foo'); + const headers = new Headers(); + const query: Record = {}; + await setAuthParams({ + auth, + headers, + query, + security: [ + { + type: 'http', + }, + ], + }); + expect(auth).toHaveBeenCalled(); + expect(headers.get('Authorization')).toBe('foo'); + expect(query).toEqual({}); + }); + it('sets first scheme only', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue('bar'); + const auth = vi.fn().mockReturnValue('foo'); const headers = new Headers(); const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'accessToken', - in: 'header', name: 'baz', + scheme: 'bearer', + type: 'http', }, { - fn: 'accessToken', in: 'query', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(headers.get('baz')).toBe('Bearer foo'); expect(Object.keys(query).length).toBe(0); }); it('sets first scheme with token', async () => { - const accessToken = vi.fn().mockReturnValue('foo'); - const apiKey = vi.fn().mockReturnValue(undefined); + const auth = vi.fn().mockImplementation((auth: Auth) => { + if (auth.type === 'apiKey') { + return; + } + return 'foo'; + }); const headers = new Headers(); const query: Record = {}; await setAuthParams({ - accessToken, - apiKey, + auth, headers, query, security: [ { - fn: 'apiKey', - in: 'header', name: 'baz', + type: 'apiKey', }, { - fn: 'accessToken', in: 'query', name: 'baz', + scheme: 'bearer', + type: 'http', }, ], }); - expect(accessToken).toHaveBeenCalled(); + expect(auth).toHaveBeenCalled(); expect(headers.get('baz')).toBeNull(); expect(query.baz).toBe('Bearer foo'); }); diff --git a/packages/client-fetch/src/index.ts b/packages/client-fetch/src/index.ts index 31dcacd23..22f8ab618 100644 --- a/packages/client-fetch/src/index.ts +++ b/packages/client-fetch/src/index.ts @@ -167,13 +167,13 @@ export const createClient = (config: Config = {}): Client => { }; export type { + Auth, Client, Config, Options, OptionsLegacyParser, RequestOptions, RequestResult, - Security, } from './types'; export { createConfig, diff --git a/packages/client-fetch/src/types.ts b/packages/client-fetch/src/types.ts index 4a66eb115..f2ed532da 100644 --- a/packages/client-fetch/src/types.ts +++ b/packages/client-fetch/src/types.ts @@ -12,17 +12,10 @@ export interface Config /** * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** * - * Access token or a function returning access token. The resolved token - * will be added to request headers where it's required. + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. */ - accessToken?: (() => Promise) | string | undefined; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * API key or a function returning API key. The resolved key will be added - * to the request payload as required. - */ - apiKey?: (() => Promise) | string | undefined; + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * Base URL for all requests made by this client. * @@ -112,6 +105,15 @@ export interface Config throwOnError?: ThrowOnError; } +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +type AuthToken = string | undefined; + export interface RequestOptions< ThrowOnError extends boolean = boolean, Url extends string = string, @@ -138,7 +140,7 @@ export interface RequestOptions< /** * Security mechanism(s) to use for the request. */ - security?: ReadonlyArray; + security?: ReadonlyArray; url: Url; } @@ -162,12 +164,6 @@ export type RequestResult< } >; -export interface Security { - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; -} - type MethodFn = < Data = unknown, TError = unknown, diff --git a/packages/client-fetch/src/utils.ts b/packages/client-fetch/src/utils.ts index fec0d26d9..b3039e5eb 100644 --- a/packages/client-fetch/src/utils.ts +++ b/packages/client-fetch/src/utils.ts @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions, Security } from './types'; +import type { Auth, Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -365,46 +365,54 @@ export const getParseAs = ( }; export const getAuthToken = async ( - security: Security, - options: Pick, + auth: Auth, + callback: RequestOptions['auth'], ): Promise => { - if (security.fn === 'accessToken') { - const token = - typeof options.accessToken === 'function' - ? await options.accessToken() - : options.accessToken; - return token ? `Bearer ${token}` : undefined; + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; } - if (security.fn === 'apiKey') { - return typeof options.apiKey === 'function' - ? await options.apiKey() - : options.apiKey; + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; } + + return token; }; export const setAuthParams = async ({ security, ...options }: Pick, 'security'> & - Pick & { + Pick & { headers: Headers; }) => { - for (const scheme of security) { - const token = await getAuthToken(scheme, options); + for (const auth of security) { + const token = await getAuthToken(auth, options.auth); if (!token) { continue; } - if (scheme.in === 'header') { - options.headers.set(scheme.name, token); - } else if (scheme.in === 'query') { - if (!options.query) { - options.query = {}; - } + const name = auth.name ?? 'Authorization'; - options.query[scheme.name] = token; + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'header': + default: + options.headers.set(name, token); + break; } return; diff --git a/packages/openapi-ts/package.json b/packages/openapi-ts/package.json index d243a4ea7..3deb4091a 100644 --- a/packages/openapi-ts/package.json +++ b/packages/openapi-ts/package.json @@ -78,7 +78,7 @@ "dependencies": { "@hey-api/json-schema-ref-parser": "1.0.1", "c12": "2.0.1", - "commander": "12.1.0", + "commander": "13.0.0", "handlebars": "4.7.8" }, "peerDependencies": { @@ -98,11 +98,11 @@ "@angular/router": "19.0.5", "@hey-api/client-axios": "workspace:*", "@hey-api/client-fetch": "workspace:*", - "@tanstack/angular-query-experimental": "5.62.9", - "@tanstack/react-query": "5.62.11", + "@tanstack/angular-query-experimental": "5.62.13", + "@tanstack/react-query": "5.62.15", "@tanstack/solid-query": "5.51.21", - "@tanstack/svelte-query": "5.62.9", - "@tanstack/vue-query": "5.62.9", + "@tanstack/svelte-query": "5.62.12", + "@tanstack/vue-query": "5.62.12", "@types/cross-spawn": "6.0.6", "@types/express": "4.17.21", "axios": "1.7.9", diff --git a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts index 48cd678b8..015a78214 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/sdk/plugin.ts @@ -27,6 +27,14 @@ import { import { serviceFunctionIdentifier } from './plugin-legacy'; import type { Config } from './types'; +// type copied from client packages +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + export const operationOptionsType = ({ identifierData, throwOnError, @@ -104,6 +112,89 @@ export const getResponseType = ( } }; +// TODO: parser - handle more security types +const securitySchemeObjectToAuthObject = ({ + securitySchemeObject, +}: { + securitySchemeObject: IR.SecurityObject; +}): Auth | undefined => { + if (securitySchemeObject.type === 'oauth2') { + // TODO: parser - handle more/multiple oauth2 flows + if (securitySchemeObject.flows.password) { + return { + scheme: 'bearer', + type: 'http', + }; + } + + return; + } + + if (securitySchemeObject.type === 'apiKey') { + if (securitySchemeObject.in === 'header') { + return { + name: securitySchemeObject.name, + type: 'apiKey', + }; + } + + // TODO: parser - support cookies auth + if (securitySchemeObject.in === 'query') { + return { + in: securitySchemeObject.in, + name: securitySchemeObject.name, + type: 'apiKey', + }; + } + + return; + } + + if (securitySchemeObject.type === 'http') { + if ( + securitySchemeObject.scheme === 'bearer' || + securitySchemeObject.scheme === 'basic' + ) { + return { + scheme: securitySchemeObject.scheme, + type: 'http', + }; + } + + return; + } +}; + +const operationAuth = ({ + operation, + plugin, +}: { + context: IR.Context; + operation: IR.OperationObject; + plugin: Plugin.Instance; +}): Array => { + if (!operation.security || !plugin.auth) { + return []; + } + + const auth: Array = []; + + for (const securitySchemeObject of operation.security) { + const authObject = securitySchemeObjectToAuthObject({ + securitySchemeObject, + }); + if (authObject) { + auth.push(authObject); + } else { + console.warn( + `❗️ SDK warning: unsupported security scheme. Please open an issue if you'd like it added https://github.com/hey-api/openapi-ts/issues\n${JSON.stringify(securitySchemeObject, null, 2)}`, + ); + } + } + + return auth; +}; + const operationStatements = ({ context, operation, @@ -209,65 +300,12 @@ const operationStatements = ({ // content type. currently impossible because successes do not contain // header information - if (operation.security && plugin.auth) { - // TODO: parser - handle more security types - // type copied from client packages - const security: Array<{ - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; - }> = []; - - for (const securitySchemeObject of operation.security) { - let supported = false; - - if (securitySchemeObject.type === 'oauth2') { - if (securitySchemeObject.flows.password) { - security.push({ - fn: 'accessToken', - in: 'header', - name: 'Authorization', - }); - - supported = true; - } - } else if (securitySchemeObject.type === 'apiKey') { - // TODO: parser - support cookies auth - if (securitySchemeObject.in !== 'cookie') { - security.push({ - fn: 'apiKey', - in: securitySchemeObject.in, - name: securitySchemeObject.name, - }); - - supported = true; - } - } else if (securitySchemeObject.type === 'http') { - if (securitySchemeObject.scheme === 'bearer') { - // Our accessToken function puts Bearer in front of the token, so it works for this scheme too - security.push({ - fn: 'accessToken', - in: 'header', - name: 'Authorization', - }); - - supported = true; - } - } - - if (!supported) { - console.warn( - `❗️ SDK warning: security scheme isn't currently supported. Please open an issue if you'd like it added https://github.com/hey-api/openapi-ts/issues\n${JSON.stringify(securitySchemeObject, null, 2)}`, - ); - } - } - - if (security.length) { - requestOptions.push({ - key: 'security', - value: compiler.arrayLiteralExpression({ elements: security }), - }); - } + const auth = operationAuth({ context, operation, plugin }); + if (auth.length) { + requestOptions.push({ + key: 'security', + value: compiler.arrayLiteralExpression({ elements: auth }), + }); } for (const name in operation.parameters?.query) { diff --git a/packages/openapi-ts/test/__snapshots__/2.0.x/security-api-key/sdk.gen.ts b/packages/openapi-ts/test/__snapshots__/2.0.x/security-api-key/sdk.gen.ts index 5bae7a23b..652d717bd 100644 --- a/packages/openapi-ts/test/__snapshots__/2.0.x/security-api-key/sdk.gen.ts +++ b/packages/openapi-ts/test/__snapshots__/2.0.x/security-api-key/sdk.gen.ts @@ -10,9 +10,9 @@ export const getFoo = (options?: Options(options?: Options) => { return (options?.client ?? client).get({ ...options, + security: [ + { + scheme: 'basic', + type: 'http' + } + ], url: '/foo' }); }; \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/2.0.x/security-oauth2/sdk.gen.ts b/packages/openapi-ts/test/__snapshots__/2.0.x/security-oauth2/sdk.gen.ts index a3425f992..fab62c794 100644 --- a/packages/openapi-ts/test/__snapshots__/2.0.x/security-oauth2/sdk.gen.ts +++ b/packages/openapi-ts/test/__snapshots__/2.0.x/security-oauth2/sdk.gen.ts @@ -10,9 +10,8 @@ export const getFoo = (options?: Options(options?: Options(options?: Options(options?: Options(options?: Options(options?: Options(options?: Options { let _config = mergeConfigs(createConfig(), config); - const instance = axios.create(_config); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { auth, ...configWithoutAuth } = _config; + const instance = axios.create(configWithoutAuth); const getConfig = (): Config => ({ ..._config }); @@ -53,8 +55,10 @@ export const createClient = (config: Config): Client => { try { // assign Axios here for consistency with fetch const _axios = opts.axios; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { auth, ...optsWithoutAuth } = opts; const response = await _axios({ - ...opts, + ...optsWithoutAuth, data: opts.body, headers: opts.headers as RawAxiosRequestHeaders, // let `paramsSerializer()` handle query params if it exists @@ -106,13 +110,13 @@ export const createClient = (config: Config): Client => { }; export type { + Auth, Client, Config, Options, OptionsLegacyParser, RequestOptions, RequestResult, - Security, } from './types'; export { createConfig, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/types.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/types.ts.snap index 8db32019c..6912b7f72 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/types.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/types.ts.snap @@ -15,21 +15,14 @@ import type { type OmitKeys = Pick>; export interface Config - extends Omit { + extends Omit { /** * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** * - * Access token or a function returning access token. The resolved token will - * be added to request payload as required. + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. */ - accessToken?: (() => Promise) | string | undefined; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * API key or a function returning API key. The resolved key will be added - * to the request payload as required. - */ - apiKey?: (() => Promise) | string | undefined; + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * Axios implementation. You can use this option to provide a custom * Axios instance. @@ -107,6 +100,15 @@ export interface Config throwOnError?: ThrowOnError; } +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +type AuthToken = string | undefined; + export interface RequestOptions< ThrowOnError extends boolean = boolean, Url extends string = string, @@ -128,7 +130,7 @@ export interface RequestOptions< /** * Security mechanism(s) to use for the request. */ - security?: ReadonlyArray; + security?: ReadonlyArray; url: Url; } @@ -143,12 +145,6 @@ export type RequestResult< | (AxiosError & { data: undefined; error: TError }) >; -export interface Security { - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; -} - type MethodFn = < Data = unknown, TError = unknown, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/utils.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/utils.ts.snap index d5dc4e6f7..e8fefad23 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/utils.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle/client/utils.ts.snap @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions, Security } from './types'; +import type { Auth, Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -319,46 +319,54 @@ export const createQuerySerializer = ({ }; export const getAuthToken = async ( - security: Security, - options: Pick, + auth: Auth, + callback: RequestOptions['auth'], ): Promise => { - if (security.fn === 'accessToken') { - const token = - typeof options.accessToken === 'function' - ? await options.accessToken() - : options.accessToken; - return token ? `Bearer ${token}` : undefined; + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; } - if (security.fn === 'apiKey') { - return typeof options.apiKey === 'function' - ? await options.apiKey() - : options.apiKey; + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; } + + return token; }; export const setAuthParams = async ({ security, ...options }: Pick, 'security'> & - Pick & { + Pick & { headers: Record; }) => { - for (const scheme of security) { - const token = await getAuthToken(scheme, options); + for (const auth of security) { + const token = await getAuthToken(auth, options.auth); if (!token) { continue; } - if (scheme.in === 'header') { - options.headers[scheme.name] = token; - } else if (scheme.in === 'query') { - if (!options.query) { - options.query = {}; - } + const name = auth.name ?? 'Authorization'; - options.query[scheme.name] = token; + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'header': + default: + options.headers[name] = token; + break; } return; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/index.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/index.ts.snap index fa2baa5f8..c13193000 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/index.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/index.ts.snap @@ -13,7 +13,9 @@ import { export const createClient = (config: Config): Client => { let _config = mergeConfigs(createConfig(), config); - const instance = axios.create(_config); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { auth, ...configWithoutAuth } = _config; + const instance = axios.create(configWithoutAuth); const getConfig = (): Config => ({ ..._config }); @@ -53,8 +55,10 @@ export const createClient = (config: Config): Client => { try { // assign Axios here for consistency with fetch const _axios = opts.axios; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { auth, ...optsWithoutAuth } = opts; const response = await _axios({ - ...opts, + ...optsWithoutAuth, data: opts.body, headers: opts.headers as RawAxiosRequestHeaders, // let `paramsSerializer()` handle query params if it exists @@ -106,13 +110,13 @@ export const createClient = (config: Config): Client => { }; export type { + Auth, Client, Config, Options, OptionsLegacyParser, RequestOptions, RequestResult, - Security, } from './types'; export { createConfig, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/types.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/types.ts.snap index 8db32019c..6912b7f72 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/types.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/types.ts.snap @@ -15,21 +15,14 @@ import type { type OmitKeys = Pick>; export interface Config - extends Omit { + extends Omit { /** * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** * - * Access token or a function returning access token. The resolved token will - * be added to request payload as required. + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. */ - accessToken?: (() => Promise) | string | undefined; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * API key or a function returning API key. The resolved key will be added - * to the request payload as required. - */ - apiKey?: (() => Promise) | string | undefined; + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * Axios implementation. You can use this option to provide a custom * Axios instance. @@ -107,6 +100,15 @@ export interface Config throwOnError?: ThrowOnError; } +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +type AuthToken = string | undefined; + export interface RequestOptions< ThrowOnError extends boolean = boolean, Url extends string = string, @@ -128,7 +130,7 @@ export interface RequestOptions< /** * Security mechanism(s) to use for the request. */ - security?: ReadonlyArray; + security?: ReadonlyArray; url: Url; } @@ -143,12 +145,6 @@ export type RequestResult< | (AxiosError & { data: undefined; error: TError }) >; -export interface Security { - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; -} - type MethodFn = < Data = unknown, TError = unknown, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/utils.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/utils.ts.snap index d5dc4e6f7..e8fefad23 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/utils.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-axios-bundle_transform/client/utils.ts.snap @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions, Security } from './types'; +import type { Auth, Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -319,46 +319,54 @@ export const createQuerySerializer = ({ }; export const getAuthToken = async ( - security: Security, - options: Pick, + auth: Auth, + callback: RequestOptions['auth'], ): Promise => { - if (security.fn === 'accessToken') { - const token = - typeof options.accessToken === 'function' - ? await options.accessToken() - : options.accessToken; - return token ? `Bearer ${token}` : undefined; + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; } - if (security.fn === 'apiKey') { - return typeof options.apiKey === 'function' - ? await options.apiKey() - : options.apiKey; + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; } + + return token; }; export const setAuthParams = async ({ security, ...options }: Pick, 'security'> & - Pick & { + Pick & { headers: Record; }) => { - for (const scheme of security) { - const token = await getAuthToken(scheme, options); + for (const auth of security) { + const token = await getAuthToken(auth, options.auth); if (!token) { continue; } - if (scheme.in === 'header') { - options.headers[scheme.name] = token; - } else if (scheme.in === 'query') { - if (!options.query) { - options.query = {}; - } + const name = auth.name ?? 'Authorization'; - options.query[scheme.name] = token; + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'header': + default: + options.headers[name] = token; + break; } return; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/index.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/index.ts.snap index 31dcacd23..22f8ab618 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/index.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/index.ts.snap @@ -167,13 +167,13 @@ export const createClient = (config: Config = {}): Client => { }; export type { + Auth, Client, Config, Options, OptionsLegacyParser, RequestOptions, RequestResult, - Security, } from './types'; export { createConfig, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/types.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/types.ts.snap index 4a66eb115..f2ed532da 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/types.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/types.ts.snap @@ -12,17 +12,10 @@ export interface Config /** * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** * - * Access token or a function returning access token. The resolved token - * will be added to request headers where it's required. + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. */ - accessToken?: (() => Promise) | string | undefined; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * API key or a function returning API key. The resolved key will be added - * to the request payload as required. - */ - apiKey?: (() => Promise) | string | undefined; + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * Base URL for all requests made by this client. * @@ -112,6 +105,15 @@ export interface Config throwOnError?: ThrowOnError; } +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +type AuthToken = string | undefined; + export interface RequestOptions< ThrowOnError extends boolean = boolean, Url extends string = string, @@ -138,7 +140,7 @@ export interface RequestOptions< /** * Security mechanism(s) to use for the request. */ - security?: ReadonlyArray; + security?: ReadonlyArray; url: Url; } @@ -162,12 +164,6 @@ export type RequestResult< } >; -export interface Security { - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; -} - type MethodFn = < Data = unknown, TError = unknown, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/utils.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/utils.ts.snap index fec0d26d9..b3039e5eb 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/utils.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle/client/utils.ts.snap @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions, Security } from './types'; +import type { Auth, Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -365,46 +365,54 @@ export const getParseAs = ( }; export const getAuthToken = async ( - security: Security, - options: Pick, + auth: Auth, + callback: RequestOptions['auth'], ): Promise => { - if (security.fn === 'accessToken') { - const token = - typeof options.accessToken === 'function' - ? await options.accessToken() - : options.accessToken; - return token ? `Bearer ${token}` : undefined; + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; } - if (security.fn === 'apiKey') { - return typeof options.apiKey === 'function' - ? await options.apiKey() - : options.apiKey; + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; } + + return token; }; export const setAuthParams = async ({ security, ...options }: Pick, 'security'> & - Pick & { + Pick & { headers: Headers; }) => { - for (const scheme of security) { - const token = await getAuthToken(scheme, options); + for (const auth of security) { + const token = await getAuthToken(auth, options.auth); if (!token) { continue; } - if (scheme.in === 'header') { - options.headers.set(scheme.name, token); - } else if (scheme.in === 'query') { - if (!options.query) { - options.query = {}; - } + const name = auth.name ?? 'Authorization'; - options.query[scheme.name] = token; + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'header': + default: + options.headers.set(name, token); + break; } return; diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/index.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/index.ts.snap index 31dcacd23..22f8ab618 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/index.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/index.ts.snap @@ -167,13 +167,13 @@ export const createClient = (config: Config = {}): Client => { }; export type { + Auth, Client, Config, Options, OptionsLegacyParser, RequestOptions, RequestResult, - Security, } from './types'; export { createConfig, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/types.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/types.ts.snap index 4a66eb115..f2ed532da 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/types.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/types.ts.snap @@ -12,17 +12,10 @@ export interface Config /** * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** * - * Access token or a function returning access token. The resolved token - * will be added to request headers where it's required. + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. */ - accessToken?: (() => Promise) | string | undefined; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * API key or a function returning API key. The resolved key will be added - * to the request payload as required. - */ - apiKey?: (() => Promise) | string | undefined; + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; /** * Base URL for all requests made by this client. * @@ -112,6 +105,15 @@ export interface Config throwOnError?: ThrowOnError; } +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +type AuthToken = string | undefined; + export interface RequestOptions< ThrowOnError extends boolean = boolean, Url extends string = string, @@ -138,7 +140,7 @@ export interface RequestOptions< /** * Security mechanism(s) to use for the request. */ - security?: ReadonlyArray; + security?: ReadonlyArray; url: Url; } @@ -162,12 +164,6 @@ export type RequestResult< } >; -export interface Security { - fn: 'accessToken' | 'apiKey'; - in: 'header' | 'query'; - name: string; -} - type MethodFn = < Data = unknown, TError = unknown, diff --git a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/utils.ts.snap b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/utils.ts.snap index fec0d26d9..b3039e5eb 100644 --- a/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/utils.ts.snap +++ b/packages/openapi-ts/test/__snapshots__/test/generated/v3-hey-api-client-fetch-bundle_transform/client/utils.ts.snap @@ -1,4 +1,4 @@ -import type { Client, Config, RequestOptions, Security } from './types'; +import type { Auth, Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -365,46 +365,54 @@ export const getParseAs = ( }; export const getAuthToken = async ( - security: Security, - options: Pick, + auth: Auth, + callback: RequestOptions['auth'], ): Promise => { - if (security.fn === 'accessToken') { - const token = - typeof options.accessToken === 'function' - ? await options.accessToken() - : options.accessToken; - return token ? `Bearer ${token}` : undefined; + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; } - if (security.fn === 'apiKey') { - return typeof options.apiKey === 'function' - ? await options.apiKey() - : options.apiKey; + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; } + + return token; }; export const setAuthParams = async ({ security, ...options }: Pick, 'security'> & - Pick & { + Pick & { headers: Headers; }) => { - for (const scheme of security) { - const token = await getAuthToken(scheme, options); + for (const auth of security) { + const token = await getAuthToken(auth, options.auth); if (!token) { continue; } - if (scheme.in === 'header') { - options.headers.set(scheme.name, token); - } else if (scheme.in === 'query') { - if (!options.query) { - options.query = {}; - } + const name = auth.name ?? 'Authorization'; - options.query[scheme.name] = token; + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'header': + default: + options.headers.set(name, token); + break; } return; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37eba7b44..ba60f7527 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -286,8 +286,8 @@ importers: specifier: workspace:* version: link:../../packages/client-fetch '@tanstack/angular-query-experimental': - specifier: 5.62.9 - version: 5.62.9(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)) + specifier: 5.62.13 + version: 5.62.13(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)) rxjs: specifier: ~7.8.0 version: 7.8.1 @@ -350,11 +350,11 @@ importers: specifier: 3.1.6 version: 3.1.6(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@tanstack/react-query': - specifier: 5.62.11 - version: 5.62.11(react@19.0.0) + specifier: 5.62.15 + version: 5.62.15(react@19.0.0) '@tanstack/react-query-devtools': - specifier: 5.62.11 - version: 5.62.11(@tanstack/react-query@5.62.11(react@19.0.0))(react@19.0.0) + specifier: 5.62.15 + version: 5.62.15(@tanstack/react-query@5.62.15(react@19.0.0))(react@19.0.0) react: specifier: 19.0.0 version: 19.0.0 @@ -414,8 +414,8 @@ importers: specifier: workspace:* version: link:../../packages/client-fetch '@tanstack/svelte-query': - specifier: 5.62.9 - version: 5.62.9(svelte@4.2.19) + specifier: 5.62.12 + version: 5.62.12(svelte@4.2.19) devDependencies: '@fontsource/fira-mono': specifier: 5.0.0 @@ -481,11 +481,11 @@ importers: specifier: workspace:* version: link:../../packages/client-fetch '@tanstack/vue-query': - specifier: 5.62.9 - version: 5.62.9(vue@3.5.13(typescript@5.5.3)) + specifier: 5.62.12 + version: 5.62.12(vue@3.5.13(typescript@5.5.3)) '@tanstack/vue-query-devtools': - specifier: 5.62.9 - version: 5.62.9(@tanstack/vue-query@5.62.9(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3)) + specifier: 5.62.12 + version: 5.62.12(@tanstack/vue-query@5.62.12(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3)) pinia: specifier: 2.3.0 version: 2.3.0(typescript@5.5.3)(vue@3.5.13(typescript@5.5.3)) @@ -588,8 +588,8 @@ importers: specifier: 2.0.1 version: 2.0.1 commander: - specifier: 12.1.0 - version: 12.1.0 + specifier: 13.0.0 + version: 13.0.0 handlebars: specifier: 4.7.8 version: 4.7.8 @@ -634,20 +634,20 @@ importers: specifier: workspace:* version: link:../client-fetch '@tanstack/angular-query-experimental': - specifier: 5.62.9 - version: 5.62.9(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)) + specifier: 5.62.13 + version: 5.62.13(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)) '@tanstack/react-query': - specifier: 5.62.11 - version: 5.62.11(react@19.0.0) + specifier: 5.62.15 + version: 5.62.15(react@19.0.0) '@tanstack/solid-query': specifier: 5.51.21 version: 5.51.21(solid-js@1.8.20) '@tanstack/svelte-query': - specifier: 5.62.9 - version: 5.62.9(svelte@4.2.19) + specifier: 5.62.12 + version: 5.62.12(svelte@4.2.19) '@tanstack/vue-query': - specifier: 5.62.9 - version: 5.62.9(vue@3.5.13(typescript@5.5.3)) + specifier: 5.62.12 + version: 5.62.12(vue@3.5.13(typescript@5.5.3)) '@types/cross-spawn': specifier: 6.0.6 version: 6.0.6 @@ -3786,8 +3786,8 @@ packages: svelte: ^4.0.0 || ^5.0.0-next.0 vite: ^5.0.0 - '@tanstack/angular-query-experimental@5.62.9': - resolution: {integrity: sha512-E74elL310zSV5K/6sv3bcPACJGR1N5dD8yK4rATo4IMiF0y34Y2zNIRwPY7Y/29JnL5ebGn4a2cLDabMY2XWsA==} + '@tanstack/angular-query-experimental@5.62.13': + resolution: {integrity: sha512-K3BCgFu7RTtKgahfCKhBwrC8hpRDqOIRGEvnASf2D7nwysm2wREjNanQZaFEETzGVSX8HGqxp+80qUb7L/cKlg==} peerDependencies: '@angular/common': '>=16.0.0' '@angular/core': '>=16.0.0' @@ -3799,20 +3799,23 @@ packages: '@tanstack/query-core@5.51.21': resolution: {integrity: sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw==} - '@tanstack/query-core@5.62.9': - resolution: {integrity: sha512-lwePd8hNYhyQ4nM/iRQ+Wz2cDtspGeZZHFZmCzHJ7mfKXt+9S301fULiY2IR2byJYY6Z03T427E5PoVfMexHjw==} + '@tanstack/query-core@5.62.12': + resolution: {integrity: sha512-6igFeBgymHkCxVgaEk+yiLwkMf9haui/EQLmI3o9CatOyDThEoFKe8toLWvWliZC/Jf+h7NwHi/zjfyLArr1ow==} + + '@tanstack/query-core@5.62.15': + resolution: {integrity: sha512-wT20X14CxcWY8YLJ/1pnsXn/y1Q2uRJZYWW93PWRtZt+3/JlGZyiyTcO4pGnqycnP7CokCROAyatsraosqZsDA==} '@tanstack/query-devtools@5.62.9': resolution: {integrity: sha512-b1NZzDLVf6laJsB1Cfm3ieuYzM+WqoO8qpm9v+3Etwd+Ph4zkhUMiT+wcWj5AhEPsXiRodKYiiW048VDNdBxNg==} - '@tanstack/react-query-devtools@5.62.11': - resolution: {integrity: sha512-i0vKgdM4ORRzqduz7UeUF52UhLrvRp4sNY/DnLsd5NqNyiKct3a0bLQMWE2fqjF5tEExQ0d0xY60ILXW/T62xA==} + '@tanstack/react-query-devtools@5.62.15': + resolution: {integrity: sha512-8aE7uD45NHZgNtHMVQC7PvM9f72mKK4bqcpHr9La8TsTRX7x8dy2Kdu2ReFNLCrdlEWkxdP5843tc/lHg+Q/rg==} peerDependencies: - '@tanstack/react-query': ^5.62.11 + '@tanstack/react-query': ^5.62.15 react: ^18 || ^19 - '@tanstack/react-query@5.62.11': - resolution: {integrity: sha512-Xb1nw0cYMdtFmwkvH9+y5yYFhXvLRCnXoqlzSw7UkqtCVFq3cG8q+rHZ2Yz1XrC+/ysUaTqbLKJqk95mCgC1oQ==} + '@tanstack/react-query@5.62.15': + resolution: {integrity: sha512-Ny3xxsOWmEQCFyHiV3CF7t6+QAV+LpBEREiXyllKR4+tStyd8smOAa98ZHmEx0ZNy36M31K8enifB5wTSYAKJw==} peerDependencies: react: ^18 || ^19 @@ -3821,19 +3824,19 @@ packages: peerDependencies: solid-js: ^1.6.0 - '@tanstack/svelte-query@5.62.9': - resolution: {integrity: sha512-2M/CpePioU4IRw1OOdA+/KwA+0swJAb3c0uipFOoWkuT11uC4tTe8UD/lYRQpJYFFn5hML7KYRxVOW0HH60XiA==} + '@tanstack/svelte-query@5.62.12': + resolution: {integrity: sha512-tG/FR6ttvf4nBeCvV/fel7KsvEJe/drAC/+WrfxgA5TzmjorSka0RoCN9TcoETcneGMXGqOD8rE9iK5z9KABUw==} peerDependencies: svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-next.0 - '@tanstack/vue-query-devtools@5.62.9': - resolution: {integrity: sha512-TBW028y+0GyiG9uDLzdE7rJUTnHp4MzHxq4uCjw2xDhyzXTq48BmqF2q5g0q2SbI9YK3Q7oltfY0asnfxx+bwQ==} + '@tanstack/vue-query-devtools@5.62.12': + resolution: {integrity: sha512-ZxFz3a56Y1MkEnPaEgiusw8/55pjIeAsYjmNBODVB1ysr5cIrW+3Wlp9TwQmLSPLF5gULO9byX5WOoqLgYErMw==} peerDependencies: - '@tanstack/vue-query': ^5.62.9 + '@tanstack/vue-query': ^5.62.12 vue: ^3.3.0 - '@tanstack/vue-query@5.62.9': - resolution: {integrity: sha512-L6soXGCGlMT5Xc/ToUNt7AGJjr6C8mc3gkASe1tDhPRyo4VoMcmnha+qf3yP4Uwd38bmZmohZwnBbuT3O3TvQA==} + '@tanstack/vue-query@5.62.12': + resolution: {integrity: sha512-/d4zBLDUcc6pNHZTIzreD7oxp2AOk+G+ImTQzZMM7GVXI2kvW86repzK12vCaB1aIdg61wbUw/ytcdC42Ki5Rw==} peerDependencies: '@vue/composition-api': ^1.1.2 vue: ^2.6.0 || ^3.3.0 @@ -5075,6 +5078,10 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@13.0.0: + resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==} + engines: {node: '>=18'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -12872,11 +12879,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/angular-query-experimental@5.62.9(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))': + '@tanstack/angular-query-experimental@5.62.13(@angular/common@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))': dependencies: '@angular/common': 19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) '@angular/core': 19.0.5(rxjs@7.8.1)(zone.js@0.15.0) - '@tanstack/query-core': 5.62.9 + '@tanstack/query-core': 5.62.12 '@tanstack/query-devtools': 5.62.9 '@tanstack/match-sorter-utils@8.19.4': @@ -12885,19 +12892,21 @@ snapshots: '@tanstack/query-core@5.51.21': {} - '@tanstack/query-core@5.62.9': {} + '@tanstack/query-core@5.62.12': {} + + '@tanstack/query-core@5.62.15': {} '@tanstack/query-devtools@5.62.9': {} - '@tanstack/react-query-devtools@5.62.11(@tanstack/react-query@5.62.11(react@19.0.0))(react@19.0.0)': + '@tanstack/react-query-devtools@5.62.15(@tanstack/react-query@5.62.15(react@19.0.0))(react@19.0.0)': dependencies: '@tanstack/query-devtools': 5.62.9 - '@tanstack/react-query': 5.62.11(react@19.0.0) + '@tanstack/react-query': 5.62.15(react@19.0.0) react: 19.0.0 - '@tanstack/react-query@5.62.11(react@19.0.0)': + '@tanstack/react-query@5.62.15(react@19.0.0)': dependencies: - '@tanstack/query-core': 5.62.9 + '@tanstack/query-core': 5.62.15 react: 19.0.0 '@tanstack/solid-query@5.51.21(solid-js@1.8.20)': @@ -12905,21 +12914,21 @@ snapshots: '@tanstack/query-core': 5.51.21 solid-js: 1.8.20 - '@tanstack/svelte-query@5.62.9(svelte@4.2.19)': + '@tanstack/svelte-query@5.62.12(svelte@4.2.19)': dependencies: - '@tanstack/query-core': 5.62.9 + '@tanstack/query-core': 5.62.12 svelte: 4.2.19 - '@tanstack/vue-query-devtools@5.62.9(@tanstack/vue-query@5.62.9(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3))': + '@tanstack/vue-query-devtools@5.62.12(@tanstack/vue-query@5.62.12(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3))': dependencies: '@tanstack/query-devtools': 5.62.9 - '@tanstack/vue-query': 5.62.9(vue@3.5.13(typescript@5.5.3)) + '@tanstack/vue-query': 5.62.12(vue@3.5.13(typescript@5.5.3)) vue: 3.5.13(typescript@5.5.3) - '@tanstack/vue-query@5.62.9(vue@3.5.13(typescript@5.5.3))': + '@tanstack/vue-query@5.62.12(vue@3.5.13(typescript@5.5.3))': dependencies: '@tanstack/match-sorter-utils': 8.19.4 - '@tanstack/query-core': 5.62.9 + '@tanstack/query-core': 5.62.12 '@vue/devtools-api': 6.6.4 vue: 3.5.13(typescript@5.5.3) vue-demi: 0.14.10(vue@3.5.13(typescript@5.5.3)) @@ -14542,6 +14551,8 @@ snapshots: commander@12.1.0: {} + commander@13.0.0: {} + commander@2.20.3: {} commander@4.1.1: {}