From e8a5aabb0be3bd194d67948fce7c34565924e0a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Aug 2026 14:56:07 +0000 Subject: [PATCH 1/2] Bump @hey-api/openapi-ts from 0.96.1 to 0.99.0 in /vue Bumps [@hey-api/openapi-ts](https://github.com/hey-api/hey-api/tree/HEAD/packages/openapi-ts) from 0.96.1 to 0.99.0. - [Release notes](https://github.com/hey-api/hey-api/releases) - [Changelog](https://github.com/hey-api/hey-api/blob/main/packages/openapi-ts/CHANGELOG.md) - [Commits](https://github.com/hey-api/hey-api/commits/@hey-api/openapi-ts@0.99.0/packages/openapi-ts) --- updated-dependencies: - dependency-name: "@hey-api/openapi-ts" dependency-version: 0.99.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- vue/package-lock.json | 105 +++++++++++++++++++++++++++++------------- vue/package.json | 2 +- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/vue/package-lock.json b/vue/package-lock.json index a846de40..4d2fe517 100644 --- a/vue/package-lock.json +++ b/vue/package-lock.json @@ -31,7 +31,7 @@ }, "devDependencies": { "@chromatic-com/storybook": "^5.2.1", - "@hey-api/openapi-ts": "^0.96.0", + "@hey-api/openapi-ts": "^0.99.0", "@playwright/test": "^1.62.1", "@storybook/addon-a11y": "^10.4.6", "@storybook/addon-docs": "^10.4.6", @@ -1189,9 +1189,9 @@ } }, "node_modules/@hey-api/codegen-core": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.8.0.tgz", - "integrity": "sha512-OuF/jenX9wz7AWHRBfb37v+jLkrfCt0FJXQuALNH2UsW6+bdZBmoibHl0K778SiHwneotJbAaEvX2S05wEqUQw==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.9.1.tgz", + "integrity": "sha512-s97jL1dgTMuiMHv2BZ1X4Tgd99Mf9GOvGdNqNcGwIMmnR+PgYNoraj4Zvp134MKsNCap/m7k0r0vKKnl56pj4w==", "dev": true, "license": "MIT", "dependencies": { @@ -1201,52 +1201,53 @@ "color-support": "1.1.3" }, "engines": { - "node": ">=22.13.0" + "node": ">=22.18.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" } }, "node_modules/@hey-api/json-schema-ref-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.4.1.tgz", - "integrity": "sha512-DoPJGxVApDlktP1yYLjmOrF0YBEqb32ieCbx1S1i09n8TyCgdoh4yQaQ3kp0sMTauH+bwNKPsFh7S8qiWCoKZA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.4.4.tgz", + "integrity": "sha512-otmd+zCxbYVBIp/mlMTnGkvlNYLkVKgs3VOIq0kSnenhB1+fRwLPQIeSwyWM6E51oXhUedkYjVsVpkVexeuJOA==", "dev": true, "license": "MIT", "dependencies": { "@jsdevtools/ono": "7.1.3", "@types/json-schema": "7.0.15", - "yaml": "2.8.3" + "js-yaml": "4.2.0" }, "engines": { - "node": ">=22.13.0" + "node": ">=22.18.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.96.1", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.96.1.tgz", - "integrity": "sha512-EnH+6ncaVC1yNvYWcsO7MoeBSqCvYZaKvRDRqh+S7dsfb9lhe2mKUR2+7sDacGUBm7VDZZ7hg4q4egxlpZaf0g==", + "version": "0.99.0", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.99.0.tgz", + "integrity": "sha512-SePU/5oEWWkvUBYmvzdYRctseoLuskyhs4ET0RvLIcmzc8yLQoA2R+KtBIQ8bPsoSUB0m4E5SmBnl6aGSA0szQ==", "dev": true, "license": "MIT", "dependencies": { - "@hey-api/codegen-core": "0.8.0", - "@hey-api/json-schema-ref-parser": "1.4.1", - "@hey-api/shared": "0.4.1", + "@hey-api/codegen-core": "0.9.1", + "@hey-api/json-schema-ref-parser": "1.4.4", + "@hey-api/shared": "0.5.0", "@hey-api/spec-types": "0.2.0", "@hey-api/types": "0.1.4", + "@lukeed/ms": "2.0.2", "ansi-colors": "4.1.3", "color-support": "1.1.3", - "commander": "14.0.3", + "commander": "15.0.0", "get-tsconfig": "4.14.0" }, "bin": { "openapi-ts": "bin/run.js" }, "engines": { - "node": ">=22.13.0" + "node": ">=22.18.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" @@ -1256,33 +1257,33 @@ } }, "node_modules/@hey-api/openapi-ts/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.12.0" } }, "node_modules/@hey-api/shared": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@hey-api/shared/-/shared-0.4.1.tgz", - "integrity": "sha512-EDAjvGpEamn2fOB7W87ZStyDSv5mEpnQCciVbiZ+Ll4EfawwIjHMMtDtRQOYol0YjTkAeEm274GFPAtg3xfPGA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@hey-api/shared/-/shared-0.5.0.tgz", + "integrity": "sha512-JN/j4Ebh4cJGYIQ5cwWuqe7GeSUyQoz7oC51WqyhKOcrejK6DKZMDkshc5d1eKTRuRL+rjozuRcoUaZZn2DGPw==", "dev": true, "license": "MIT", "dependencies": { - "@hey-api/codegen-core": "0.8.0", - "@hey-api/json-schema-ref-parser": "1.4.1", + "@hey-api/codegen-core": "0.9.1", + "@hey-api/json-schema-ref-parser": "1.4.4", "@hey-api/spec-types": "0.2.0", "@hey-api/types": "0.1.4", "ansi-colors": "4.1.3", "cross-spawn": "7.0.6", "open": "11.0.0", - "semver": "7.7.4" + "semver": "7.8.4" }, "engines": { - "node": ">=22.13.0" + "node": ">=22.18.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" @@ -1528,6 +1529,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@mdx-js/react": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", @@ -5551,6 +5562,13 @@ "node": ">=18" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -8610,6 +8628,29 @@ "dev": true, "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsdoc-type-pratt-parser": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-9.1.1.tgz", @@ -11375,9 +11416,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "dev": true, "license": "ISC", "bin": { diff --git a/vue/package.json b/vue/package.json index 9e934704..c27790eb 100644 --- a/vue/package.json +++ b/vue/package.json @@ -98,7 +98,7 @@ }, "devDependencies": { "@chromatic-com/storybook": "^5.2.1", - "@hey-api/openapi-ts": "^0.96.0", + "@hey-api/openapi-ts": "^0.99.0", "@playwright/test": "^1.62.1", "@storybook/addon-a11y": "^10.4.6", "@storybook/addon-docs": "^10.4.6", From 24f81d3138da9acb26a61d291a410337acb7168e Mon Sep 17 00:00:00 2001 From: Samuel Monroe Date: Thu, 15 Oct 2026 10:02:04 +0200 Subject: [PATCH 2/2] chore(vue): adapt to @hey-api/openapi-ts 0.99 - Regenerate the demo OpenAPI client with `npm run openapi-codegen` (then prettier, matching the committed formatting). 0.99 reshapes the generated SDK/types and adds `core/queryKeySerializer.gen.ts`; the `client` export used by `src/demo/main.ts` is unchanged. - Override js-yaml to ^4.3.2 under @hey-api/json-schema-ref-parser. 1.4.4 pins js-yaml 4.2.0, which is flagged high by GHSA-52cp-r559-cp3m, GHSA-5p4m-2wfm-xmqj and GHSA-2883-xcg3-v3hh and fails the Dependencies Audit. 4.3.2 is a same-major patch release that fixes all three. Co-Authored-By: Claude Opus 5.5 (1M context) --- vue/package-lock.json | 60 +++++----- vue/package.json | 5 +- vue/src/demo/openapi/client.gen.ts | 17 +-- vue/src/demo/openapi/client/client.gen.ts | 27 +++-- vue/src/demo/openapi/client/index.ts | 4 +- vue/src/demo/openapi/client/types.gen.ts | 38 +++--- vue/src/demo/openapi/client/utils.gen.ts | 47 ++++---- vue/src/demo/openapi/core/auth.gen.ts | 9 ++ .../demo/openapi/core/bodySerializer.gen.ts | 28 +++-- vue/src/demo/openapi/core/params.gen.ts | 74 ++++++++---- .../demo/openapi/core/pathSerializer.gen.ts | 12 +- .../openapi/core/queryKeySerializer.gen.ts | 112 ++++++++++++++++++ .../demo/openapi/core/serverSentEvents.gen.ts | 10 +- vue/src/demo/openapi/core/types.gen.ts | 8 +- vue/src/demo/openapi/core/utils.gen.ts | 37 +++++- vue/src/demo/openapi/index.ts | 33 +++++- vue/src/demo/openapi/sdk.gen.ts | 45 +++---- vue/src/demo/openapi/types.gen.ts | 8 +- 18 files changed, 401 insertions(+), 173 deletions(-) create mode 100644 vue/src/demo/openapi/core/queryKeySerializer.gen.ts diff --git a/vue/package-lock.json b/vue/package-lock.json index b329ce65..c4007ba5 100644 --- a/vue/package-lock.json +++ b/vue/package-lock.json @@ -1211,36 +1211,6 @@ "url": "https://github.com/sponsors/hey-api" } }, - "node_modules/@hey-api/json-schema-ref-parser/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/@hey-api/json-schema-ref-parser/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@hey-api/openapi-ts": { "version": "0.99.0", "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.99.0.tgz", @@ -8441,6 +8411,36 @@ "dev": true, "license": "MIT" }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/jsdoc-type-pratt-parser": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-9.1.2.tgz", diff --git a/vue/package.json b/vue/package.json index 50c76c51..cea05131 100644 --- a/vue/package.json +++ b/vue/package.json @@ -155,6 +155,9 @@ ], "overrides": { "minimatch": "10.2.3", - "@parcel/watcher": "^2.6.0" + "@parcel/watcher": "^2.6.0", + "@hey-api/json-schema-ref-parser": { + "js-yaml": "^4.3.2" + } } } diff --git a/vue/src/demo/openapi/client.gen.ts b/vue/src/demo/openapi/client.gen.ts index 54354dc6..afecea60 100644 --- a/vue/src/demo/openapi/client.gen.ts +++ b/vue/src/demo/openapi/client.gen.ts @@ -1,12 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { ClientOptions } from './types.gen' -import { - type Config, - type ClientOptions as DefaultClientOptions, - createClient, - createConfig, -} from './client' +import { type Client, type ClientOptions, type Config, createClient, createConfig } from './client' +import type { ClientOptions as ClientOptions2 } from './types.gen' /** * The `createClientConfig()` function will be called on client initialization @@ -16,8 +11,8 @@ import { * `setConfig()`. This is useful for example if you're using Next.js * to ensure your client always has the correct values. */ -export type CreateClientConfig = ( - override?: Config -) => Config & T> +export type CreateClientConfig = ( + override?: Config +) => Config & T> -export const client = createClient(createConfig()) +export const client: Client = createClient(createConfig()) diff --git a/vue/src/demo/openapi/client/client.gen.ts b/vue/src/demo/openapi/client/client.gen.ts index 1c55e34d..318be1b5 100644 --- a/vue/src/demo/openapi/client/client.gen.ts +++ b/vue/src/demo/openapi/client/client.gen.ts @@ -5,6 +5,7 @@ import axios from 'axios' import { createSseClient } from '../core/serverSentEvents.gen' import type { HttpMethod } from '../core/types.gen' +import { getValidRequestBody } from '../core/utils.gen' import type { Client, Config, RequestOptions } from './types.gen' import { buildUrl, createConfig, mergeConfigs, mergeHeaders, setAuthParams } from './utils.gen' @@ -34,7 +35,13 @@ export const createClient = (config: Config = {}): Client => { return getConfig() } - const beforeRequest = async (options: RequestOptions) => { + const beforeRequest = async < + TData = unknown, + ThrowOnError extends boolean = boolean, + Url extends string = string, + >( + options: RequestOptions + ) => { const opts = { ..._config, ...options, @@ -43,17 +50,14 @@ export const createClient = (config: Config = {}): Client => { } if (opts.security) { - await setAuthParams({ - ...opts, - security: opts.security, - }) + await setAuthParams(opts) } if (opts.requestValidator) { await opts.requestValidator(opts) } - if (opts.body && opts.bodySerializer) { + if (opts.body !== undefined && opts.bodySerializer) { opts.body = opts.bodySerializer(opts.body) } @@ -64,7 +68,6 @@ export const createClient = (config: Config = {}): Client => { // @ts-expect-error const request: Client['request'] = async (options) => { - // @ts-expect-error const { opts, url } = await beforeRequest(options) try { // assign Axios here for consistency with fetch @@ -73,8 +76,8 @@ export const createClient = (config: Config = {}): Client => { const { auth, ...optsWithoutAuth } = opts const response = await _axios({ ...optsWithoutAuth, - baseURL: opts.baseURL as string, - data: opts.body, + baseURL: '', // the baseURL is already included in `url` + data: getValidRequestBody(opts), headers: opts.headers as RawAxiosRequestHeaders, // let `paramsSerializer()` handle query params if it exists params: opts.paramsSerializer ? opts.query : undefined, @@ -118,14 +121,18 @@ export const createClient = (config: Config = {}): Client => { body: opts.body as BodyInit | null | undefined, headers: opts.headers as Record, method, + serializedBody: getValidRequestBody(opts) as BodyInit | null | undefined, // @ts-expect-error signal: opts.signal, url, }) } + const _buildUrl: Client['buildUrl'] = (options) => + buildUrl({ axios: instance, ..._config, ...options }) + return { - buildUrl, + buildUrl: _buildUrl, connect: makeMethodFn('CONNECT'), delete: makeMethodFn('DELETE'), get: makeMethodFn('GET'), diff --git a/vue/src/demo/openapi/client/index.ts b/vue/src/demo/openapi/client/index.ts index 85e48f44..ef659f23 100644 --- a/vue/src/demo/openapi/client/index.ts +++ b/vue/src/demo/openapi/client/index.ts @@ -8,6 +8,9 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen' export { buildClientParams } from '../core/params.gen' +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen' +export type { ServerSentEventsResult } from '../core/serverSentEvents.gen' +export type { ClientMeta } from '../core/types.gen' export { createClient } from './client.gen' export type { Client, @@ -15,7 +18,6 @@ export type { Config, CreateClientConfig, Options, - OptionsLegacyParser, RequestOptions, RequestResult, TDataShape, diff --git a/vue/src/demo/openapi/client/types.gen.ts b/vue/src/demo/openapi/client/types.gen.ts index 3576fbfc..4d805ab3 100644 --- a/vue/src/demo/openapi/client/types.gen.ts +++ b/vue/src/demo/openapi/client/types.gen.ts @@ -18,6 +18,7 @@ export interface Config /** * Axios implementation. You can use this option to provide either an * `AxiosStatic` or an `AxiosInstance`. + * * @default axios */ axios?: AxiosStatic | AxiosInstance @@ -39,6 +40,7 @@ export interface Config > /** * Throw an error instead of returning it in the response? + * * @default false */ throwOnError?: T['throwOnError'] @@ -55,6 +57,7 @@ export interface RequestOptions< }>, Pick< ServerSentEventsOptions, + | 'onRequest' | 'onSseError' | 'onSseEvent' | 'sseDefaultRetryDelay' @@ -101,9 +104,14 @@ type MethodFn = , 'method'> ) => RequestResult -type SseFn = ( - options: Omit, 'method'> -) => Promise> +type SseFn = < + TData = unknown, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _TError = unknown, + ThrowOnError extends boolean = false, +>( + options: Omit, 'method'> +) => Promise> type RequestFn = ( options: Omit, 'method'> & @@ -112,13 +120,16 @@ type RequestFn = query?: Record url: string }, >( - options: Pick & Omit, 'axios'> + options: TData & + Pick< + RequestOptions, + 'axios' | 'baseURL' | 'paramsSerializer' | 'querySerializer' + > ) => string export type Client = CoreClient & { @@ -152,19 +163,4 @@ export type Options< ThrowOnError extends boolean = boolean, TResponse = unknown, > = OmitKeys, 'body' | 'path' | 'query' | 'url'> & - Omit - -export type OptionsLegacyParser< - TData = unknown, - ThrowOnError extends boolean = boolean, -> = TData extends { body?: any } - ? TData extends { headers?: any } - ? OmitKeys, 'body' | 'headers' | 'url'> & TData - : OmitKeys, 'body' | 'url'> & - TData & - Pick, 'headers'> - : TData extends { headers?: any } - ? OmitKeys, 'headers' | 'url'> & - TData & - Pick, 'body'> - : OmitKeys, 'url'> & TData + ([TData] extends [never] ? unknown : Omit) diff --git a/vue/src/demo/openapi/client/utils.gen.ts b/vue/src/demo/openapi/client/utils.gen.ts index cd4c3651..6eefa234 100644 --- a/vue/src/demo/openapi/client/utils.gen.ts +++ b/vue/src/demo/openapi/client/utils.gen.ts @@ -11,11 +11,10 @@ import { getUrl } from '../core/utils.gen' import type { Client, ClientOptions, Config, RequestOptions } from './types.gen' export const createQuerySerializer = ({ - allowReserved, - array, - object, -}: QuerySerializerOptions = {}) => { - const querySerializer = (queryParams: T) => { + parameters = {}, + ...args +}: QuerySerializerOptions = {}): ((queryParams: T) => string) => { + const querySerializer = (queryParams: T): string => { const search: string[] = [] if (queryParams && typeof queryParams === 'object') { for (const name in queryParams) { @@ -25,29 +24,31 @@ export const createQuerySerializer = ({ continue } + const options = parameters[name] || args + if (Array.isArray(value)) { const serializedArray = serializeArrayParam({ - allowReserved, + allowReserved: options.allowReserved, explode: true, name, style: 'form', value, - ...array, + ...options.array, }) if (serializedArray) search.push(serializedArray) } else if (typeof value === 'object') { const serializedObject = serializeObjectParam({ - allowReserved, + allowReserved: options.allowReserved, explode: true, name, style: 'deepObject', value: value as Record, - ...object, + ...options.object, }) if (serializedObject) search.push(serializedObject) } else { const serializedPrimitive = serializePrimitiveParam({ - allowReserved, + allowReserved: options.allowReserved, name, value: value as string, }) @@ -82,14 +83,12 @@ const checkForExistence = ( return false } -export const setAuthParams = async ({ - security, - ...options -}: Pick, 'security'> & - Pick & { +export async function setAuthParams( + options: Pick & { headers: Record - }) => { - for (const auth of security) { + } +): Promise { + for (const auth of options.security ?? []) { if (checkForExistence(options, auth.name)) { continue } @@ -125,9 +124,14 @@ export const setAuthParams = async ({ } } -export const buildUrl: Client['buildUrl'] = (options) => - getUrl({ - baseUrl: options.baseURL as string, +export const buildUrl: Client['buildUrl'] = (options) => { + const instanceBaseUrl = options.axios?.defaults?.baseURL + + const baseUrl = + options.baseURL && typeof options.baseURL === 'string' ? options.baseURL : instanceBaseUrl + + return getUrl({ + baseUrl: baseUrl as string, path: options.path, // let `paramsSerializer()` handle query params if it exists query: !options.paramsSerializer ? options.query : undefined, @@ -137,6 +141,7 @@ export const buildUrl: Client['buildUrl'] = (options) => : createQuerySerializer(options.querySerializer), url: options.url, }) +} export const mergeConfigs = (a: Config, b: Config): Config => { const config = { ...a, ...b } @@ -185,7 +190,7 @@ export const mergeHeaders = ( mergedHeaders[key] = [...(mergedHeaders[key] ?? []), v as string] } } else if (value !== undefined) { - // assume object headers are meant to be JSON stringified, i.e. their + // assume object headers are meant to be JSON stringified, i.e., their // content value in OpenAPI specification is 'application/json' mergedHeaders[key] = typeof value === 'object' ? JSON.stringify(value) : (value as string) } diff --git a/vue/src/demo/openapi/core/auth.gen.ts b/vue/src/demo/openapi/core/auth.gen.ts index 997fe588..cbeab22f 100644 --- a/vue/src/demo/openapi/core/auth.gen.ts +++ b/vue/src/demo/openapi/core/auth.gen.ts @@ -5,11 +5,20 @@ export type AuthToken = string | undefined export interface Auth { /** * Which part of the request do we use to send the auth? + * * @default 'header' */ in?: 'header' | 'query' | 'cookie' + /** + * A unique identifier for the security scheme. + * + * Defined only when there are multiple security schemes whose `Auth` + * shape would otherwise be identical. + */ + key?: string /** * Header or query parameter name. + * * @default 'Authorization' */ name?: string diff --git a/vue/src/demo/openapi/core/bodySerializer.gen.ts b/vue/src/demo/openapi/core/bodySerializer.gen.ts index a0d20ab4..c6322199 100644 --- a/vue/src/demo/openapi/core/bodySerializer.gen.ts +++ b/vue/src/demo/openapi/core/bodySerializer.gen.ts @@ -4,12 +4,20 @@ import type { ArrayStyle, ObjectStyle, SerializerOptions } from './pathSerialize export type QuerySerializer = (query: Record) => string -export type BodySerializer = (body: any) => any +export type BodySerializer = (body: unknown) => unknown -export interface QuerySerializerOptions { +type QuerySerializerOptionsObject = { allowReserved?: boolean - array?: SerializerOptions - object?: SerializerOptions + array?: Partial> + object?: Partial> +} + +export type QuerySerializerOptions = QuerySerializerOptionsObject & { + /** + * Per-parameter serialization overrides. When provided, these settings + * override the global array/object settings for specific parameter names. + */ + parameters?: Record } const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => { @@ -31,12 +39,10 @@ const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: } export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T - ): FormData => { + bodySerializer: (body: unknown): FormData => { const data = new FormData() - Object.entries(body).forEach(([key, value]) => { + Object.entries(body as Record).forEach(([key, value]) => { if (value === undefined || value === null) { return } @@ -52,15 +58,15 @@ export const formDataBodySerializer = { } export const jsonBodySerializer = { - bodySerializer: (body: T): string => + bodySerializer: (body: unknown): string => JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)), } export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>(body: T): string => { + bodySerializer: (body: unknown): string => { const data = new URLSearchParams() - Object.entries(body).forEach(([key, value]) => { + Object.entries(body as Record).forEach(([key, value]) => { if (value === undefined || value === null) { return } diff --git a/vue/src/demo/openapi/core/params.gen.ts b/vue/src/demo/openapi/core/params.gen.ts index fb5d2526..0c1f1f0b 100644 --- a/vue/src/demo/openapi/core/params.gen.ts +++ b/vue/src/demo/openapi/core/params.gen.ts @@ -23,6 +23,17 @@ export type Field = key?: string map?: string } + | { + /** + * Field name. This is the name we want the user to see and use. + */ + key: string + /** + * Field mapped name. This is the name we want to use in the request. + * If `in` is omitted, `map` aliases `key` to the transport layer. + */ + map: Slot + } export interface Fields { allowExtra?: Partial> @@ -41,13 +52,17 @@ const extraPrefixes = Object.entries(extraPrefixesMap) type KeyMap = Map< string, - { - in: Slot - map?: string - } + | { + in: Slot + map?: string + } + | { + in?: never + map: Slot + } > -const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { +function buildKeyMap(fields: FieldsConfig, map?: KeyMap): KeyMap { if (!map) { map = new Map() } @@ -60,6 +75,10 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { map: config.map, }) } + } else if ('key' in config) { + map.set(config.key, { + map: config.map, + }) } else if (config.args) { buildKeyMap(config.args, map) } @@ -69,30 +88,39 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { } interface Params { - body: unknown + body?: unknown headers: Record path: Record query: Record } -const stripEmptySlots = (params: Params) => { +function stripEmptySlots(params: Params): void { for (const [slot, value] of Object.entries(params)) { - if (value && typeof value === 'object' && !Object.keys(value).length) { + if (slot === 'body') continue + if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) { delete params[slot as Slot] } } } -export const buildClientParams = (args: ReadonlyArray, fields: FieldsConfig) => { +export function buildClientParams(args: ReadonlyArray, fields: FieldsConfig): Params { const params: Params = { - body: {}, - headers: {}, - path: {}, - query: {}, + headers: Object.create(null), + path: Object.create(null), + query: Object.create(null), } const map = buildKeyMap(fields) + function writeSlot(slot: Slot, key: string, value: unknown): void { + let record = params[slot] as Record | undefined + if (record === undefined) { + record = Object.create(null) as Record + params[slot] = record + } + record[key] = value + } + let config: FieldsConfig[number] | undefined for (const [index, arg] of args.entries()) { @@ -108,7 +136,9 @@ export const buildClientParams = (args: ReadonlyArray, fields: FieldsCo if (config.key) { const field = map.get(config.key)! const name = field.map || config.key - ;(params[field.in] as Record)[name] = arg + if (field.in) { + writeSlot(field.in, name, arg) + } } else { params.body = arg } @@ -117,18 +147,22 @@ export const buildClientParams = (args: ReadonlyArray, fields: FieldsCo const field = map.get(key) if (field) { - const name = field.map || key - ;(params[field.in] as Record)[name] = value + if (field.in) { + const name = field.map || key + writeSlot(field.in, name, value) + } else { + params[field.map] = value + } } else { const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix)) if (extra) { const [prefix, slot] = extra - ;(params[slot] as Record)[key.slice(prefix.length)] = value - } else { - for (const [slot, allowed] of Object.entries(config.allowExtra ?? {})) { + writeSlot(slot, key.slice(prefix.length), value) + } else if ('allowExtra' in config && config.allowExtra) { + for (const [slot, allowed] of Object.entries(config.allowExtra)) { if (allowed) { - ;(params[slot as Slot] as Record)[key] = value + writeSlot(slot as Slot, key, value) break } } diff --git a/vue/src/demo/openapi/core/pathSerializer.gen.ts b/vue/src/demo/openapi/core/pathSerializer.gen.ts index 6aac0a2e..82b9b6ac 100644 --- a/vue/src/demo/openapi/core/pathSerializer.gen.ts +++ b/vue/src/demo/openapi/core/pathSerializer.gen.ts @@ -25,7 +25,7 @@ interface SerializePrimitiveParam extends SerializePrimitiveOptions { value: string } -export const separatorArrayExplode = (style: ArraySeparatorStyle) => { +export const separatorArrayExplode = (style: ArraySeparatorStyle): '.' | ';' | ',' | '&' => { switch (style) { case 'label': return '.' @@ -38,7 +38,7 @@ export const separatorArrayExplode = (style: ArraySeparatorStyle) => { } } -export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { +export const separatorArrayNoExplode = (style: ArraySeparatorStyle): ',' | '|' | '%20' => { switch (style) { case 'form': return ',' @@ -51,7 +51,7 @@ export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { } } -export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { +export const separatorObjectExplode = (style: ObjectSeparatorStyle): '.' | ';' | ',' | '&' => { switch (style) { case 'label': return '.' @@ -72,7 +72,7 @@ export const serializeArrayParam = ({ value, }: SerializeOptions & { value: unknown[] -}) => { +}): string => { if (!explode) { const joinedValues = ( allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) @@ -110,7 +110,7 @@ export const serializePrimitiveParam = ({ allowReserved, name, value, -}: SerializePrimitiveParam) => { +}: SerializePrimitiveParam): string => { if (value === undefined || value === null) { return '' } @@ -134,7 +134,7 @@ export const serializeObjectParam = ({ }: SerializeOptions & { value: Record | Date valueOnly?: boolean -}) => { +}): string => { if (value instanceof Date) { return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}` } diff --git a/vue/src/demo/openapi/core/queryKeySerializer.gen.ts b/vue/src/demo/openapi/core/queryKeySerializer.gen.ts new file mode 100644 index 00000000..a2e53426 --- /dev/null +++ b/vue/src/demo/openapi/core/queryKeySerializer.gen.ts @@ -0,0 +1,112 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue } + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown): unknown | undefined => { + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined + } + if (typeof value === 'bigint') { + return value.toString() + } + if (value instanceof Date) { + return value.toISOString() + } + return value +} + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer) + if (json === undefined) { + return undefined + } + return JSON.parse(json) as JsonValue + } catch { + return undefined + } +} + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false + } + const prototype = Object.getPrototypeOf(value as object) + return prototype === Object.prototype || prototype === null +} + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)) + const result: Record = {} + + for (const [key, value] of entries) { + const existing = result[key] + if (existing === undefined) { + result[key] = value + continue + } + + if (Array.isArray(existing)) { + ;(existing as string[]).push(value) + } else { + result[key] = [existing, value] + } + } + + return result +} + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => { + if (value === null) { + return null + } + + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return value + } + + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined + } + + if (typeof value === 'bigint') { + return value.toString() + } + + if (value instanceof Date) { + return value.toISOString() + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value) + } + + if (typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams) { + return serializeSearchParams(value) + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value) + } + + return undefined +} diff --git a/vue/src/demo/openapi/core/serverSentEvents.gen.ts b/vue/src/demo/openapi/core/serverSentEvents.gen.ts index 100310e0..b47eced3 100644 --- a/vue/src/demo/openapi/core/serverSentEvents.gen.ts +++ b/vue/src/demo/openapi/core/serverSentEvents.gen.ts @@ -7,6 +7,7 @@ export type ServerSentEventsOptions = Omit = Omit void @@ -25,6 +27,7 @@ export type ServerSentEventsOptions = Omit = Omit = Omit } -export const createSseClient = ({ +export function createSseClient({ onRequest, onSseError, onSseEvent, @@ -86,7 +91,7 @@ export const createSseClient = ({ sseSleepFn, url, ...options -}: ServerSentEventsOptions): ServerSentEventsResult => { +}: ServerSentEventsOptions): ServerSentEventsResult { let lastEventId: string | undefined const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))) @@ -150,6 +155,7 @@ export const createSseClient = ({ const { done, value } = await reader.read() if (done) break buffer += value + buffer = buffer.replace(/\r\n?/g, '\n') // normalize line endings const chunks = buffer.split('\n\n') buffer = chunks.pop() ?? '' diff --git a/vue/src/demo/openapi/core/types.gen.ts b/vue/src/demo/openapi/core/types.gen.ts index 55eaf114..077eca3d 100644 --- a/vue/src/demo/openapi/core/types.gen.ts +++ b/vue/src/demo/openapi/core/types.gen.ts @@ -72,7 +72,7 @@ export interface Config { requestValidator?: (data: unknown) => Promise /** * A function transforming response data before it's returned. This is useful - * for post-processing data, e.g. converting ISO strings into Date objects. + * for post-processing data, e.g., converting ISO strings into Date objects. */ responseTransformer?: (data: unknown) => Promise /** @@ -83,6 +83,12 @@ export interface Config { responseValidator?: (data: unknown) => Promise } +/** + * Arbitrary metadata passed through the `meta` request option. + */ +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface ClientMeta {} + type IsExactlyNeverOrNeverUndefined = [T] extends [never] ? true : [T] extends [never | undefined] diff --git a/vue/src/demo/openapi/core/utils.gen.ts b/vue/src/demo/openapi/core/utils.gen.ts index 96e0d1f6..ec5ec1e9 100644 --- a/vue/src/demo/openapi/core/utils.gen.ts +++ b/vue/src/demo/openapi/core/utils.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { QuerySerializer } from './bodySerializer.gen' +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen' import { type ArraySeparatorStyle, serializeArrayParam, @@ -13,9 +13,9 @@ export interface PathSerializer { url: string } -export const PATH_PARAM_RE = /\{[^{}]+\}/g +export const PATH_PARAM_RE: RegExp = /\{[^{}]+\}/g -export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer): string => { let url = _url const matches = _url.match(PATH_PARAM_RE) if (matches) { @@ -94,7 +94,7 @@ export const getUrl = ({ query?: Record querySerializer: QuerySerializer url: string -}) => { +}): string => { const pathUrl = _url.startsWith('/') ? _url : `/${_url}` let url = (baseUrl ?? '') + pathUrl if (path) { @@ -109,3 +109,32 @@ export const getUrl = ({ } return url } + +export function getValidRequestBody(options: { + body?: unknown + bodySerializer?: BodySerializer | null + serializedBody?: unknown +}): unknown { + const hasBody = options.body !== undefined + const isSerializedBody = hasBody && options.bodySerializer + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== '' + + return hasSerializedBody ? options.serializedBody : null + } + + // not all clients implement a serializedBody property (i.e., client-axios) + return options.body !== '' ? options.body : null + } + + // plain/text body + if (hasBody) { + return options.body + } + + // no body was provided + return undefined +} diff --git a/vue/src/demo/openapi/index.ts b/vue/src/demo/openapi/index.ts index 16b44b61..1c292ad1 100644 --- a/vue/src/demo/openapi/index.ts +++ b/vue/src/demo/openapi/index.ts @@ -1,3 +1,32 @@ // This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen' -export * from './sdk.gen' + +export { + getHello1, + getHello2, + getHello3, + type Options, + postHello1, + postHello2, + postHello3, +} from './sdk.gen' +export type { + ClientOptions, + GetHello1Data, + GetHello1Response, + GetHello1Responses, + GetHello2Data, + GetHello2Response, + GetHello2Responses, + GetHello3Data, + GetHello3Response, + GetHello3Responses, + PostHello1Data, + PostHello1Response, + PostHello1Responses, + PostHello2Data, + PostHello2Response, + PostHello2Responses, + PostHello3Data, + PostHello3Response, + PostHello3Responses, +} from './types.gen' diff --git a/vue/src/demo/openapi/sdk.gen.ts b/vue/src/demo/openapi/sdk.gen.ts index eb8a0af9..8210faea 100644 --- a/vue/src/demo/openapi/sdk.gen.ts +++ b/vue/src/demo/openapi/sdk.gen.ts @@ -1,6 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Options as ClientOptions, TDataShape, Client } from './client' +import type { Client, ClientMeta, Options as Options2, RequestResult, TDataShape } from './client' +import { client } from './client.gen' import type { GetHello1Data, GetHello1Responses, @@ -15,12 +16,12 @@ import type { PostHello3Data, PostHello3Responses, } from './types.gen' -import { client as _heyApiClient } from './client.gen' export type Options< TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, -> = ClientOptions & { + TResponse = unknown, +> = Options2 & { /** * You can provide a client instance returned by `createClient()` instead of * individual options. This might be also useful if you want to implement a @@ -31,87 +32,76 @@ export type Options< * You can pass arbitrary values through the `meta` object. This can be * used to access values that aren't defined as part of the SDK function. */ - meta?: Record + meta?: keyof ClientMeta extends never ? Record : ClientMeta } /** * Basic GET (no input, no output) - * @param options */ export const getHello1 = ( options?: Options -) => { - return (options?.client ?? _heyApiClient).get({ +): RequestResult => + (options?.client ?? client).get({ responseType: 'json', url: '/basic-get', ...options, }) -} /** * Simple GET (simple input/output) - * @param options */ export const getHello2 = ( options: Options -) => { - return (options.client ?? _heyApiClient).get({ +): RequestResult => + (options.client ?? client).get({ responseType: 'json', url: '/simple-get', ...options, }) -} /** * Complex GET (complex input/output) - * @param options */ export const getHello3 = ( options: Options -) => { - return (options.client ?? _heyApiClient).get({ +): RequestResult => + (options.client ?? client).get({ responseType: 'json', url: '/complex-get/{userId}', ...options, }) -} /** * Basic POST (no input, no output) - * @param options */ export const postHello1 = ( options?: Options -) => { - return (options?.client ?? _heyApiClient).post({ +): RequestResult => + (options?.client ?? client).post({ responseType: 'json', url: '/basic-post', ...options, }) -} /** * Simple POST (simple input/output) - * @param options */ export const postHello2 = ( options: Options -) => { - return (options.client ?? _heyApiClient).post({ +): RequestResult => + (options.client ?? client).post({ responseType: 'json', url: '/simple-post', ...options, }) -} /** * Complex POST (complex input/output) - * @param options */ export const postHello3 = ( options: Options -) => { - return (options.client ?? _heyApiClient).post({ +): RequestResult => + (options.client ?? client).post({ responseType: 'json', url: '/complex-post/{itemId}', ...options, @@ -120,4 +110,3 @@ export const postHello3 = ( ...options.headers, }, }) -} diff --git a/vue/src/demo/openapi/types.gen.ts b/vue/src/demo/openapi/types.gen.ts index 86b2cb4f..181a8a0e 100644 --- a/vue/src/demo/openapi/types.gen.ts +++ b/vue/src/demo/openapi/types.gen.ts @@ -1,5 +1,9 @@ // This file is auto-generated by @hey-api/openapi-ts +export type ClientOptions = { + baseURL: `${string}://${string}` | (string & {}) +} + export type GetHello1Data = { body?: never path?: never @@ -142,7 +146,3 @@ export type PostHello3Responses = { } export type PostHello3Response = PostHello3Responses[keyof PostHello3Responses] - -export type ClientOptions = { - baseURL: `${string}://${string}` | (string & {}) -}