diff --git a/.changeset/eight-squids-crash.md b/.changeset/eight-squids-crash.md new file mode 100644 index 000000000..55a57f7af --- /dev/null +++ b/.changeset/eight-squids-crash.md @@ -0,0 +1,8 @@ +--- +'@hey-api/client-axios': patch +'@hey-api/client-fetch': patch +'@hey-api/client-nuxt': patch +'@hey-api/openapi-ts': patch +--- + +fix: bundle clients from compiled index file diff --git a/examples/openapi-ts-sample/openapi-ts.config.ts b/examples/openapi-ts-sample/openapi-ts.config.ts index 2470f065a..0fa71a5f0 100644 --- a/examples/openapi-ts-sample/openapi-ts.config.ts +++ b/examples/openapi-ts-sample/openapi-ts.config.ts @@ -2,8 +2,7 @@ import { defineConfig } from '@hey-api/openapi-ts'; export default defineConfig({ client: '@hey-api/client-fetch', - input: - '../../packages/openapi-ts/test/spec/2.0.x/body-response-text-plain.yaml', + input: '../../packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml', output: { format: 'prettier', lint: 'eslint', diff --git a/examples/openapi-ts-sample/src/App.tsx b/examples/openapi-ts-sample/src/App.tsx index dbd10f3bb..ffdd655a2 100644 --- a/examples/openapi-ts-sample/src/App.tsx +++ b/examples/openapi-ts-sample/src/App.tsx @@ -14,7 +14,9 @@ import { postFoo } from './client/sdk.gen'; function App() { const onClick = async () => { postFoo({ - body: 'foo', + body: { + foo: [[1, 2]], + }, }); }; diff --git a/examples/openapi-ts-sample/src/client/sdk.gen.ts b/examples/openapi-ts-sample/src/client/sdk.gen.ts index eb9f289e4..2db23f6e1 100644 --- a/examples/openapi-ts-sample/src/client/sdk.gen.ts +++ b/examples/openapi-ts-sample/src/client/sdk.gen.ts @@ -3,22 +3,23 @@ import { createClient, createConfig, + formDataBodySerializer, type Options, } from '@hey-api/client-fetch'; -import type { PostFooData, PostFooResponse } from './types.gen'; +import type { PostFooData } from './types.gen'; export const client = createClient(createConfig()); export const postFoo = ( options: Options, ) => - (options?.client ?? client).post({ - bodySerializer: null, + (options?.client ?? client).post({ + ...formDataBodySerializer, url: '/foo', ...options, headers: { - 'Content-Type': 'text/plain', + 'Content-Type': null, ...options?.headers, }, }); diff --git a/examples/openapi-ts-sample/src/client/types.gen.ts b/examples/openapi-ts-sample/src/client/types.gen.ts index bdb7d6bc0..5efd5c5d7 100644 --- a/examples/openapi-ts-sample/src/client/types.gen.ts +++ b/examples/openapi-ts-sample/src/client/types.gen.ts @@ -1,7 +1,9 @@ // This file is auto-generated by @hey-api/openapi-ts export type PostFooData = { - body: string; + body: { + foo: Array>; + }; path?: never; query?: never; url: '/foo'; @@ -11,7 +13,5 @@ export type PostFooResponses = { /** * OK */ - 200: string; + 200: unknown; }; - -export type PostFooResponse = PostFooResponses[keyof PostFooResponses]; diff --git a/package.json b/package.json index 1664e4512..2480ca7ba 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,8 @@ "husky": "9.1.7", "lint-staged": "15.3.0", "prettier": "3.4.2", + "rollup": "4.31.0", + "rollup-plugin-dts": "6.1.1", "tsup": "8.3.5", "typescript": "5.5.3", "typescript-eslint": "8.19.1", diff --git a/packages/client-axios/package.json b/packages/client-axios/package.json index 5b35ff742..d20324ba3 100644 --- a/packages/client-axios/package.json +++ b/packages/client-axios/package.json @@ -54,7 +54,7 @@ "src" ], "scripts": { - "build": "tsup && pnpm check-exports", + "build": "tsup && rollup -c && pnpm check-exports", "check-exports": "attw --pack .", "dev": "tsup --watch", "prepublishOnly": "pnpm build", @@ -68,6 +68,7 @@ "axios": ">= 1.0.0 < 2" }, "devDependencies": { + "@hey-api/client-core": "workspace:*", "axios": "1.7.9" } } diff --git a/packages/client-axios/rollup.config.mjs b/packages/client-axios/rollup.config.mjs new file mode 100644 index 000000000..02907e214 --- /dev/null +++ b/packages/client-axios/rollup.config.mjs @@ -0,0 +1,30 @@ +import path from 'node:path'; + +import { defineConfig } from 'rollup'; +import dts from 'rollup-plugin-dts'; + +const files = ['index.d.ts', 'index.d.cts']; + +export default files.map((file) => + defineConfig({ + external: (id) => { + const normalizedId = id.split(path.sep).join('/'); + if (normalizedId === '@hey-api/client-core') { + return false; + } + return ( + !normalizedId.startsWith('/') && !/^[a-zA-Z]:\//.test(normalizedId) + ); + }, + input: `./dist/${file}`, + output: { + file: `./dist/${file}`, + format: 'es', + }, + plugins: [ + dts({ + respectExternal: true, + }), + ], + }), +); diff --git a/packages/client-axios/src/__tests__/utils.test.ts b/packages/client-axios/src/__tests__/utils.test.ts index d1ab1c93e..f0323e3ed 100644 --- a/packages/client-axios/src/__tests__/utils.test.ts +++ b/packages/client-axios/src/__tests__/utils.test.ts @@ -1,62 +1,7 @@ +import type { Auth } from '@hey-api/client-core'; import { describe, expect, it, vi } from 'vitest'; -import type { Auth } from '../types'; -import { - axiosHeadersKeywords, - getAuthToken, - mergeHeaders, - setAuthParams, -} from '../utils'; - -describe('getAuthToken', () => { - it('returns bearer token', async () => { - const auth = vi.fn().mockReturnValue('foo'); - const token = await getAuthToken( - { - scheme: 'bearer', - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe('Bearer foo'); - }); - - it('returns basic token', async () => { - const auth = vi.fn().mockReturnValue('foo:bar'); - const token = await getAuthToken( - { - scheme: 'basic', - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe(`Basic ${btoa('foo:bar')}`); - }); - - it('returns raw token', async () => { - const auth = vi.fn().mockReturnValue('foo'); - const token = await getAuthToken( - { - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe('foo'); - }); - - it('returns nothing when auth function is undefined', async () => { - const token = await getAuthToken( - { - type: 'http', - }, - undefined, - ); - expect(token).toBeUndefined(); - }); -}); +import { axiosHeadersKeywords, mergeHeaders, setAuthParams } from '../utils'; describe('mergeHeaders', () => { it.each(axiosHeadersKeywords)( diff --git a/packages/client-axios/src/index.ts b/packages/client-axios/src/index.ts index c13193000..a5538bd02 100644 --- a/packages/client-axios/src/index.ts +++ b/packages/client-axios/src/index.ts @@ -110,7 +110,6 @@ export const createClient = (config: Config): Client => { }; export type { - Auth, Client, Config, Options, @@ -118,9 +117,10 @@ export type { RequestOptions, RequestResult, } from './types'; +export { createConfig } from './utils'; +export type { Auth, QuerySerializerOptions } from '@hey-api/client-core'; export { - createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, -} from './utils'; +} from '@hey-api/client-core'; diff --git a/packages/client-axios/src/types.ts b/packages/client-axios/src/types.ts index 501a4a291..987ee0832 100644 --- a/packages/client-axios/src/types.ts +++ b/packages/client-axios/src/types.ts @@ -1,3 +1,9 @@ +import type { + Auth, + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from '@hey-api/client-core'; import type { AxiosError, AxiosInstance, @@ -6,12 +12,6 @@ import type { CreateAxiosDefaults, } from 'axios'; -import type { - BodySerializer, - QuerySerializer, - QuerySerializerOptions, -} from './utils'; - type OmitKeys = Pick>; export interface Config @@ -96,13 +96,6 @@ 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< diff --git a/packages/client-axios/src/utils.ts b/packages/client-axios/src/utils.ts index e8fefad23..46663d7e6 100644 --- a/packages/client-axios/src/utils.ts +++ b/packages/client-axios/src/utils.ts @@ -1,4 +1,16 @@ -import type { Auth, Client, Config, RequestOptions } from './types'; +import type { + ArraySeparatorStyle, + QuerySerializer, + QuerySerializerOptions, +} from '@hey-api/client-core'; +import { + getAuthToken, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '@hey-api/client-core'; + +import type { Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -7,188 +19,6 @@ interface PathSerializer { const PATH_PARAM_RE = /\{[^{}]+\}/g; -type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; -type MatrixStyle = 'label' | 'matrix' | 'simple'; -type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type ObjectStyle = 'form' | 'deepObject'; -type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; - -export type QuerySerializer = (query: Record) => string; - -export type BodySerializer = (body: any) => any; - -interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; -} - -interface SerializeOptions - extends SerializePrimitiveOptions, - SerializerOptions {} -interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; -} -interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; -} - -export interface QuerySerializerOptions { - allowReserved?: boolean; - array?: SerializerOptions; - object?: SerializerOptions; -} - -const serializePrimitiveParam = ({ - allowReserved, - name, - value, -}: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ''; - } - - if (typeof value === 'object') { - throw new Error( - 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', - ); - } - - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; -}; - -const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; - default: - return ','; - } -}; - -const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: unknown[]; -}) => { - if (!explode) { - const joinedValues = ( - allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) - ).join(separatorArrayNoExplode(style)); - switch (style) { - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - case 'simple': - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } - - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === 'label' || style === 'simple') { - return allowReserved ? v : encodeURIComponent(v as string); - } - - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: Record; -}) => { - if (style !== 'deepObject' && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [ - ...values, - key, - allowReserved ? (v as string) : encodeURIComponent(v as string), - ]; - }); - const joinedValues = values.join(','); - switch (style) { - case 'form': - return `${name}=${joinedValues}`; - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } - - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { let url = _url; const matches = _url.match(PATH_PARAM_RE); @@ -318,28 +148,6 @@ export const createQuerySerializer = ({ return querySerializer; }; -export const getAuthToken = async ( - auth: Auth, - callback: RequestOptions['auth'], -): Promise => { - const token = - typeof callback === 'function' ? await callback(auth) : callback; - - if (!token) { - return; - } - - if (auth.scheme === 'bearer') { - return `Bearer ${token}`; - } - - if (auth.scheme === 'basic') { - return `Basic ${btoa(token)}`; - } - - return token; -}; - export const setAuthParams = async ({ security, ...options @@ -413,18 +221,6 @@ export const getUrl = ({ return url; }; -const serializeFormDataPair = ( - formData: FormData, - key: string, - value: unknown, -) => { - if (typeof value === 'string' || value instanceof Blob) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } -}; - export const mergeConfigs = (a: Config, b: Config): Config => { const config = { ...a, ...b }; config.headers = mergeHeaders(a.headers, b.headers); @@ -484,64 +280,6 @@ export const mergeHeaders = ( return mergedHeaders; }; -export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const formData = new FormData(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(formData, key, v)); - } else { - serializeFormDataPair(formData, key, value); - } - }); - - return formData; - }, -}; - -export const jsonBodySerializer = { - bodySerializer: (body: T) => JSON.stringify(body), -}; - -const serializeUrlSearchParamsPair = ( - data: URLSearchParams, - key: string, - value: unknown, -) => { - if (typeof value === 'string') { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new URLSearchParams(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); - - return data; - }, -}; - export const createConfig = (override: Config = {}): Config => ({ baseURL: '', ...override, diff --git a/packages/client-core/LICENSE.md b/packages/client-core/LICENSE.md new file mode 100644 index 000000000..6acd8004b --- /dev/null +++ b/packages/client-core/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Hey API + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/client-core/package.json b/packages/client-core/package.json index 97581fa34..7e7bb82eb 100644 --- a/packages/client-core/package.json +++ b/packages/client-core/package.json @@ -2,7 +2,6 @@ "name": "@hey-api/client-core", "version": "0.1.2", "private": true, - "type": "module", "description": "Core utilities for type-safe @hey-api/openapi-ts clients", "homepage": "https://heyapi.dev/", "repository": { @@ -13,17 +12,43 @@ "url": "https://github.com/hey-api/openapi-ts/issues" }, "license": "MIT", + "author": { + "email": "lubos@heyapi.dev", + "name": "Hey API", + "url": "https://heyapi.dev" + }, + "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { - "import": "./dist/node/index.mjs", - "require": "./dist/node/index.cjs", - "types": "./dist/node/index.d.ts" + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./package.json": "./package.json" }, + "sideEffects": false, + "files": [ + "dist", + "LICENSE.md", + "src" + ], "scripts": { + "build": "tsup && pnpm check-exports", + "check-exports": "attw --pack .", + "dev": "tsup --watch", "prepublishOnly": "pnpm build", - "test:coverage:disabled": "vitest run --coverage", - "test:update:disabled": "vitest watch --update", - "test:watch:disabled": "vitest watch", - "test:disabled": "vitest run", - "typecheck:disabled": "tsc --noEmit" + "test:coverage": "vitest run --coverage", + "test:update": "vitest watch --update", + "test:watch": "vitest watch", + "test": "vitest run", + "typecheck": "vitest --typecheck --watch=false" } } diff --git a/packages/client-core/src/__tests__/auth.test.ts b/packages/client-core/src/__tests__/auth.test.ts new file mode 100644 index 000000000..491bce17f --- /dev/null +++ b/packages/client-core/src/__tests__/auth.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { getAuthToken } from '../auth'; + +describe('getAuthToken', () => { + it('returns bearer token', async () => { + const auth = vi.fn().mockReturnValue('foo'); + const token = await getAuthToken( + { + scheme: 'bearer', + type: 'http', + }, + auth, + ); + expect(auth).toHaveBeenCalled(); + expect(token).toBe('Bearer foo'); + }); + + it('returns basic token', async () => { + const auth = vi.fn().mockReturnValue('foo:bar'); + const token = await getAuthToken( + { + scheme: 'basic', + type: 'http', + }, + auth, + ); + expect(auth).toHaveBeenCalled(); + expect(token).toBe(`Basic ${btoa('foo:bar')}`); + }); + + it('returns raw token', async () => { + const auth = vi.fn().mockReturnValue('foo'); + const token = await getAuthToken( + { + type: 'http', + }, + auth, + ); + expect(auth).toHaveBeenCalled(); + expect(token).toBe('foo'); + }); + + it('returns nothing when auth function is undefined', async () => { + const token = await getAuthToken( + { + type: 'http', + }, + undefined, + ); + expect(token).toBeUndefined(); + }); +}); diff --git a/packages/client-core/src/__tests__/index.test.ts b/packages/client-core/src/__tests__/index.test.ts deleted file mode 100644 index 76ce44875..000000000 --- a/packages/client-core/src/__tests__/index.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -describe('client core', () => { - it('works', () => { - expect(1).toBe(1); - }); -}); diff --git a/packages/client-core/src/auth.ts b/packages/client-core/src/auth.ts new file mode 100644 index 000000000..2c6c703ac --- /dev/null +++ b/packages/client-core/src/auth.ts @@ -0,0 +1,30 @@ +type AuthToken = string | undefined; + +export interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/packages/client-core/src/bodySerializer.ts b/packages/client-core/src/bodySerializer.ts new file mode 100644 index 000000000..2a15d99ea --- /dev/null +++ b/packages/client-core/src/bodySerializer.ts @@ -0,0 +1,81 @@ +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: any) => any; + +export interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} + +const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +) => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T, + ) => { + const data = new FormData(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: T) => JSON.stringify(body), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T, + ) => { + const data = new URLSearchParams(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data; + }, +}; diff --git a/packages/client-core/src/cancelablePromise.ts b/packages/client-core/src/cancelablePromise.ts deleted file mode 100644 index 79da79e98..000000000 --- a/packages/client-core/src/cancelablePromise.ts +++ /dev/null @@ -1,126 +0,0 @@ -export class CancelError extends Error { - constructor(message: string) { - super(message); - this.name = 'CancelError'; - } - - public get isCancelled(): boolean { - return true; - } -} - -export interface OnCancel { - readonly isCancelled: boolean; - readonly isRejected: boolean; - readonly isResolved: boolean; - - (cancelHandler: () => void): void; -} - -export class CancelablePromise implements Promise { - private _isResolved: boolean; - private _isRejected: boolean; - private _isCancelled: boolean; - readonly cancelHandlers: (() => void)[]; - readonly promise: Promise; - private _resolve?: (value: T | PromiseLike) => void; - private _reject?: (reason?: unknown) => void; - - constructor( - executor: ( - resolve: (value: T | PromiseLike) => void, - reject: (reason?: unknown) => void, - onCancel: OnCancel, - ) => void, - ) { - this._isResolved = false; - this._isRejected = false; - this._isCancelled = false; - this.cancelHandlers = []; - this.promise = new Promise((resolve, reject) => { - this._resolve = resolve; - this._reject = reject; - - const onResolve = (value: T | PromiseLike): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this._isResolved = true; - if (this._resolve) this._resolve(value); - }; - - const onReject = (reason?: unknown): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this._isRejected = true; - if (this._reject) this._reject(reason); - }; - - const onCancel = (cancelHandler: () => void): void => { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this.cancelHandlers.push(cancelHandler); - }; - - Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this._isResolved, - }); - - Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this._isRejected, - }); - - Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this._isCancelled, - }); - - return executor(onResolve, onReject, onCancel as OnCancel); - }); - } - - get [Symbol.toStringTag]() { - return 'Cancellable Promise'; - } - - public then( - onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onRejected?: ((reason: unknown) => TResult2 | PromiseLike) | null, - ): Promise { - return this.promise.then(onFulfilled, onRejected); - } - - public catch( - onRejected?: ((reason: unknown) => TResult | PromiseLike) | null, - ): Promise { - return this.promise.catch(onRejected); - } - - public finally(onFinally?: (() => void) | null): Promise { - return this.promise.finally(onFinally); - } - - public cancel(): void { - if (this._isResolved || this._isRejected || this._isCancelled) { - return; - } - this._isCancelled = true; - if (this.cancelHandlers.length) { - try { - for (const cancelHandler of this.cancelHandlers) { - cancelHandler(); - } - } catch (error) { - console.warn('Cancellation threw an error', error); - return; - } - } - this.cancelHandlers.length = 0; - if (this._reject) this._reject(new CancelError('Request aborted')); - } - - public get isCancelled(): boolean { - return this._isCancelled; - } -} diff --git a/packages/client-core/src/index.ts b/packages/client-core/src/index.ts index 66c251b1b..f4be51b9b 100644 --- a/packages/client-core/src/index.ts +++ b/packages/client-core/src/index.ts @@ -1,263 +1,26 @@ -import type { - ApiRequestOptions, - ApiResult, - Headers, - Middleware, - Resolver, -} from './types'; - -export { CancelablePromise, CancelError, OnCancel } from './cancelablePromise'; - -export class ApiError extends Error { - public readonly url: string; - public readonly status: number; - public readonly statusText: string; - public readonly body: unknown; - public readonly request: ApiRequestOptions; - - constructor( - request: ApiRequestOptions, - response: ApiResult, - message: string, - ) { - super(message); - - this.name = 'ApiError'; - this.url = response.url; - this.status = response.status; - this.statusText = response.statusText; - this.body = response.body; - this.request = request; - } -} - -export class Interceptors { - _fns: Middleware[]; - - constructor() { - this._fns = []; - } - - eject(fn: Middleware) { - const index = this._fns.indexOf(fn); - if (index !== -1) { - this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; - } - } - - use(fn: Middleware) { - this._fns = [...this._fns, fn]; - } -} - -export type OpenAPIConfig = { - BASE: string; - CREDENTIALS: 'include' | 'omit' | 'same-origin'; - ENCODE_PATH?: ((path: string) => string) | undefined; - HEADERS?: Headers | Resolver | undefined; - PASSWORD?: string | Resolver | undefined; - TOKEN?: string | Resolver | undefined; - USERNAME?: string | Resolver | undefined; - VERSION: string; - WITH_CREDENTIALS: boolean; - interceptors: { - request: Interceptors; - response: Interceptors; - }; -}; - -export const OpenAPI: OpenAPIConfig = { - BASE: '{{{server}}}', - CREDENTIALS: 'include', - ENCODE_PATH: undefined, - HEADERS: undefined, - PASSWORD: undefined, - TOKEN: undefined, - USERNAME: undefined, - VERSION: '{{{version}}}', - WITH_CREDENTIALS: false, - interceptors: { - request: new Interceptors(), - response: new Interceptors(), - }, -}; - -export const isString = (value: unknown): value is string => - typeof value === 'string'; - -export const isStringWithValue = (value: unknown): value is string => - isString(value) && value !== ''; - -export const isBlob = (value: any): value is Blob => value instanceof Blob; - -export const isFormData = (value: unknown): value is FormData => - value instanceof FormData; - -export const isSuccess = (status: number): boolean => - status >= 200 && status < 300; - -export const base64 = (str: string): string => { - try { - return btoa(str); - } catch { - return Buffer.from(str).toString('base64'); - } -}; - -export const getQueryString = (params: Record): string => { - const qs: string[] = []; - - const append = (key: string, value: unknown) => { - qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); - }; - - const encodePair = (key: string, value: unknown) => { - if (value === undefined || value === null) { - return; - } - - if (value instanceof Date) { - append(key, value.toISOString()); - } else if (Array.isArray(value)) { - value.forEach((v) => encodePair(key, v)); - } else if (typeof value === 'object') { - Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v)); - } else { - append(key, value); - } - }; - - Object.entries(params).forEach(([key, value]) => encodePair(key, value)); - - return qs.length ? `?${qs.join('&')}` : ''; -}; - -export const getUrl = ( - config: OpenAPIConfig, - options: ApiRequestOptions, -): string => { - const encoder = config.ENCODE_PATH || encodeURI; - - const path = options.url - .replace('{api-version}', config.VERSION) - .replace(/{(.*?)}/g, (substring: string, group: string) => { - if (options.path?.hasOwnProperty(group)) { - return encoder(String(options.path[group])); - } - return substring; - }); - - const url = config.BASE + path; - return options.query ? url + getQueryString(options.query) : url; -}; - -export const getFormData = ( - options: ApiRequestOptions, -): FormData | undefined => { - if (options.formData) { - const formData = new FormData(); - - const process = (key: string, value: unknown) => { - if (isString(value) || isBlob(value)) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } - }; - - Object.entries(options.formData) - .filter(([, value]) => value !== undefined && value !== null) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - value.forEach((v) => process(key, v)); - } else { - process(key, value); - } - }); - - return formData; - } - return undefined; -}; - -export const resolve = async ( - options: ApiRequestOptions, - resolver?: T | Resolver, -): Promise => { - if (typeof resolver === 'function') { - return (resolver as Resolver)(options); - } - return resolver; -}; - -export const catchErrorCodes = ( - options: ApiRequestOptions, - result: ApiResult, -): void => { - const errors: Record = { - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Payload Too Large', - 414: 'URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Range Not Satisfiable', - 417: 'Expectation Failed', - 418: 'Im a teapot', - 421: 'Misdirected Request', - 422: 'Unprocessable Content', - 423: 'Locked', - 424: 'Failed Dependency', - 425: 'Too Early', - 426: 'Upgrade Required', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - 451: 'Unavailable For Legal Reasons', - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 506: 'Variant Also Negotiates', - 507: 'Insufficient Storage', - 508: 'Loop Detected', - 510: 'Not Extended', - 511: 'Network Authentication Required', - ...options.errors, - }; - - const error = errors[result.status]; - if (error) { - throw new ApiError(options, result, error); - } - - if (!result.ok) { - const errorStatus = result.status ?? 'unknown'; - const errorStatusText = result.statusText ?? 'unknown'; - const errorBody = (() => { - try { - return JSON.stringify(result.body, null, 2); - } catch { - return undefined; - } - })(); - - throw new ApiError( - options, - result, - `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`, - ); - } -}; +export type { Auth } from './auth'; +export { getAuthToken } from './auth'; +export type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from './bodySerializer'; +export type { + ArraySeparatorStyle, + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer'; +export { + separatorArrayExplode, + separatorArrayNoExplode, + separatorObjectExplode, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer'; diff --git a/packages/client-core/src/node/index.ts b/packages/client-core/src/node/index.ts deleted file mode 100644 index ca2c8d7e8..000000000 --- a/packages/client-core/src/node/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export { - ApiError, - base64, - CancelablePromise, - CancelError, - catchErrorCodes, - getFormData, - getQueryString, - getUrl, - Interceptors, - isBlob, - isFormData, - isString, - isStringWithValue, - isSuccess, - OnCancel, - OpenAPI, - type OpenAPIConfig, - resolve, -} from '../'; -export type { ApiRequestOptions, ApiResult } from '../types'; diff --git a/packages/client-core/src/node/tsconfig.json b/packages/client-core/src/node/tsconfig.json deleted file mode 100644 index 7a9b791b8..000000000 --- a/packages/client-core/src/node/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "lib": ["ES2020", "DOM"], - "stripInternal": true - }, - "exclude": ["../**/__tests__"], - "extends": "../../tsconfig.base.json", - "include": ["../"] -} diff --git a/packages/client-core/src/pathSerializer.ts b/packages/client-core/src/pathSerializer.ts new file mode 100644 index 000000000..b15ce18a8 --- /dev/null +++ b/packages/client-core/src/pathSerializer.ts @@ -0,0 +1,177 @@ +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: Record | Date; +}) => { + if (value instanceof Date) { + return `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; diff --git a/packages/client-core/src/types.ts b/packages/client-core/src/types.ts deleted file mode 100644 index ab3380199..000000000 --- a/packages/client-core/src/types.ts +++ /dev/null @@ -1,35 +0,0 @@ -export type ApiResult = { - readonly body: TData; - readonly ok: boolean; - readonly status: number; - readonly statusText: string; - readonly url: string; -}; - -export type ApiRequestOptions = { - readonly body?: any; - readonly cookies?: Record; - readonly errors?: Record; - readonly formData?: Record | any[]; - readonly headers?: Record; - readonly mediaType?: string; - readonly method: - | 'CONNECT' - | 'DELETE' - | 'GET' - | 'HEAD' - | 'OPTIONS' - | 'PATCH' - | 'POST' - | 'PUT' - | 'TRACE'; - readonly path?: Record; - readonly query?: Record; - readonly responseHeader?: string; - readonly responseTransformer?: (data: unknown) => Promise; - readonly url: string; -}; - -export type Headers = Record; -export type Middleware = (value: T) => T | Promise; -export type Resolver = (options: ApiRequestOptions) => Promise; diff --git a/packages/client-core/tsconfig.base.json b/packages/client-core/tsconfig.base.json index 64e31b3e5..4ae154dda 100644 --- a/packages/client-core/tsconfig.base.json +++ b/packages/client-core/tsconfig.base.json @@ -5,6 +5,7 @@ "module": "ESNext", "moduleResolution": "Bundler", "noImplicitOverride": true, + "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "strict": true, "target": "ES2022", diff --git a/packages/client-core/tsconfig.json b/packages/client-core/tsconfig.json index c2a684570..faaf0992f 100644 --- a/packages/client-core/tsconfig.json +++ b/packages/client-core/tsconfig.json @@ -3,7 +3,5 @@ "compilerOptions": { "declaration": false, "esModuleInterop": true - }, - "exclude": ["node_modules", "**/__mocks__"], - "include": ["./src/**/*.ts"] + } } diff --git a/packages/client-core/tsup.config.ts b/packages/client-core/tsup.config.ts new file mode 100644 index 000000000..77dfaf864 --- /dev/null +++ b/packages/client-core/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig((options) => ({ + clean: true, + dts: true, + entry: ['src/index.ts'], + format: ['cjs', 'esm'], + minify: !options.watch, + shims: false, + sourcemap: true, + treeshake: true, +})); diff --git a/packages/client-fetch/package.json b/packages/client-fetch/package.json index 26b38eb58..b848e62b4 100644 --- a/packages/client-fetch/package.json +++ b/packages/client-fetch/package.json @@ -54,7 +54,7 @@ "src" ], "scripts": { - "build": "tsup && pnpm check-exports", + "build": "tsup && rollup -c && pnpm check-exports", "check-exports": "attw --pack .", "dev": "tsup --watch", "prepublishOnly": "pnpm build", @@ -63,5 +63,8 @@ "test:watch": "vitest watch", "test": "vitest run", "typecheck": "vitest --typecheck --watch=false" + }, + "devDependencies": { + "@hey-api/client-core": "workspace:*" } } diff --git a/packages/client-fetch/rollup.config.mjs b/packages/client-fetch/rollup.config.mjs new file mode 100644 index 000000000..02907e214 --- /dev/null +++ b/packages/client-fetch/rollup.config.mjs @@ -0,0 +1,30 @@ +import path from 'node:path'; + +import { defineConfig } from 'rollup'; +import dts from 'rollup-plugin-dts'; + +const files = ['index.d.ts', 'index.d.cts']; + +export default files.map((file) => + defineConfig({ + external: (id) => { + const normalizedId = id.split(path.sep).join('/'); + if (normalizedId === '@hey-api/client-core') { + return false; + } + return ( + !normalizedId.startsWith('/') && !/^[a-zA-Z]:\//.test(normalizedId) + ); + }, + input: `./dist/${file}`, + output: { + file: `./dist/${file}`, + format: 'es', + }, + plugins: [ + dts({ + respectExternal: true, + }), + ], + }), +); diff --git a/packages/client-fetch/src/__tests__/utils.test.ts b/packages/client-fetch/src/__tests__/utils.test.ts index 1f21382ab..17f378755 100644 --- a/packages/client-fetch/src/__tests__/utils.test.ts +++ b/packages/client-fetch/src/__tests__/utils.test.ts @@ -1,57 +1,7 @@ +import type { Auth } from '@hey-api/client-core'; import { describe, expect, it, vi } from 'vitest'; -import type { Auth } from '../types'; -import { getAuthToken, getParseAs, setAuthParams } from '../utils'; - -describe('getAuthToken', () => { - it('returns bearer token', async () => { - const auth = vi.fn().mockReturnValue('foo'); - const token = await getAuthToken( - { - scheme: 'bearer', - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe('Bearer foo'); - }); - - it('returns basic token', async () => { - const auth = vi.fn().mockReturnValue('foo:bar'); - const token = await getAuthToken( - { - scheme: 'basic', - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe(`Basic ${btoa('foo:bar')}`); - }); - - it('returns raw token', async () => { - const auth = vi.fn().mockReturnValue('foo'); - const token = await getAuthToken( - { - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe('foo'); - }); - - it('returns nothing when auth function is undefined', async () => { - const token = await getAuthToken( - { - type: 'http', - }, - undefined, - ); - expect(token).toBeUndefined(); - }); -}); +import { getParseAs, setAuthParams } from '../utils'; describe('getParseAs', () => { const scenarios: Array<{ diff --git a/packages/client-fetch/src/index.ts b/packages/client-fetch/src/index.ts index 22f8ab618..0b0453f56 100644 --- a/packages/client-fetch/src/index.ts +++ b/packages/client-fetch/src/index.ts @@ -167,7 +167,6 @@ export const createClient = (config: Config = {}): Client => { }; export type { - Auth, Client, Config, Options, @@ -175,10 +174,10 @@ export type { RequestOptions, RequestResult, } from './types'; +export { createConfig } from './utils'; +export type { Auth, QuerySerializerOptions } from '@hey-api/client-core'; export { - createConfig, formDataBodySerializer, jsonBodySerializer, - type QuerySerializerOptions, urlSearchParamsBodySerializer, -} from './utils'; +} from '@hey-api/client-core'; diff --git a/packages/client-fetch/src/types.ts b/packages/client-fetch/src/types.ts index d48f7e59d..f64f3d7a9 100644 --- a/packages/client-fetch/src/types.ts +++ b/packages/client-fetch/src/types.ts @@ -1,9 +1,11 @@ import type { + Auth, BodySerializer, - Middleware, QuerySerializer, QuerySerializerOptions, -} from './utils'; +} from '@hey-api/client-core'; + +import type { Middleware } from './utils'; type OmitKeys = Pick>; @@ -101,13 +103,6 @@ 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< diff --git a/packages/client-fetch/src/utils.ts b/packages/client-fetch/src/utils.ts index b3039e5eb..f92862cf7 100644 --- a/packages/client-fetch/src/utils.ts +++ b/packages/client-fetch/src/utils.ts @@ -1,4 +1,16 @@ -import type { Auth, Client, Config, RequestOptions } from './types'; +import type { + QuerySerializer, + QuerySerializerOptions, +} from '@hey-api/client-core'; +import { + getAuthToken, + jsonBodySerializer, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '@hey-api/client-core'; + +import type { Client, Config, RequestOptions } from './types'; interface PathSerializer { path: Record; @@ -10,188 +22,6 @@ const PATH_PARAM_RE = /\{[^{}]+\}/g; type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; type MatrixStyle = 'label' | 'matrix' | 'simple'; type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type ObjectStyle = 'form' | 'deepObject'; -type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; - -export type QuerySerializer = (query: Record) => string; - -export type BodySerializer = (body: any) => any; - -interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; -} - -interface SerializeOptions - extends SerializePrimitiveOptions, - SerializerOptions {} -interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; -} -interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; -} - -export interface QuerySerializerOptions { - allowReserved?: boolean; - array?: SerializerOptions; - object?: SerializerOptions; -} - -const serializePrimitiveParam = ({ - allowReserved, - name, - value, -}: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ''; - } - - if (typeof value === 'object') { - throw new Error( - 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', - ); - } - - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; -}; - -const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; - default: - return ','; - } -}; - -const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: unknown[]; -}) => { - if (!explode) { - const joinedValues = ( - allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) - ).join(separatorArrayNoExplode(style)); - switch (style) { - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - case 'simple': - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } - - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === 'label' || style === 'simple') { - return allowReserved ? v : encodeURIComponent(v as string); - } - - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: Record | Date; -}) => { - if (value instanceof Date) { - return `${name}=${value.toISOString()}`; - } - - if (style !== 'deepObject' && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [ - ...values, - key, - allowReserved ? (v as string) : encodeURIComponent(v as string), - ]; - }); - const joinedValues = values.join(','); - switch (style) { - case 'form': - return `${name}=${joinedValues}`; - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } - - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { let url = _url; @@ -364,28 +194,6 @@ export const getParseAs = ( } }; -export const getAuthToken = async ( - auth: Auth, - callback: RequestOptions['auth'], -): Promise => { - const token = - typeof callback === 'function' ? await callback(auth) : callback; - - if (!token) { - return; - } - - if (auth.scheme === 'bearer') { - return `Bearer ${token}`; - } - - if (auth.scheme === 'basic') { - return `Basic ${btoa(token)}`; - } - - return token; -}; - export const setAuthParams = async ({ security, ...options @@ -568,72 +376,6 @@ export const createInterceptors = () => ({ response: new Interceptors>(), }); -const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { - if (typeof value === 'string' || value instanceof Blob) { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new FormData(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(data, key, v)); - } else { - serializeFormDataPair(data, key, value); - } - }); - - return data; - }, -}; - -export const jsonBodySerializer = { - bodySerializer: (body: T) => JSON.stringify(body), -}; - -const serializeUrlSearchParamsPair = ( - data: URLSearchParams, - key: string, - value: unknown, -) => { - if (typeof value === 'string') { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new URLSearchParams(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); - - return data; - }, -}; - const defaultQuerySerializer = createQuerySerializer({ allowReserved: false, array: { diff --git a/packages/client-nuxt/package.json b/packages/client-nuxt/package.json index bb2baec8f..037787582 100644 --- a/packages/client-nuxt/package.json +++ b/packages/client-nuxt/package.json @@ -55,7 +55,7 @@ "src" ], "scripts": { - "build": "tsup && pnpm check-exports", + "build": "tsup && rollup -c && pnpm check-exports", "check-exports": "attw --pack .", "dev": "tsup --watch", "prepublishOnly": "pnpm build", @@ -70,6 +70,7 @@ "vue": ">= 3.5.13 < 4" }, "devDependencies": { + "@hey-api/client-core": "workspace:*", "@nuxt/test-utils": "3.15.1" } } diff --git a/packages/client-nuxt/rollup.config.mjs b/packages/client-nuxt/rollup.config.mjs new file mode 100644 index 000000000..02907e214 --- /dev/null +++ b/packages/client-nuxt/rollup.config.mjs @@ -0,0 +1,30 @@ +import path from 'node:path'; + +import { defineConfig } from 'rollup'; +import dts from 'rollup-plugin-dts'; + +const files = ['index.d.ts', 'index.d.cts']; + +export default files.map((file) => + defineConfig({ + external: (id) => { + const normalizedId = id.split(path.sep).join('/'); + if (normalizedId === '@hey-api/client-core') { + return false; + } + return ( + !normalizedId.startsWith('/') && !/^[a-zA-Z]:\//.test(normalizedId) + ); + }, + input: `./dist/${file}`, + output: { + file: `./dist/${file}`, + format: 'es', + }, + plugins: [ + dts({ + respectExternal: true, + }), + ], + }), +); diff --git a/packages/client-nuxt/src/__tests__/utils.test.ts b/packages/client-nuxt/src/__tests__/utils.test.ts index 97e88f7ac..158a98271 100644 --- a/packages/client-nuxt/src/__tests__/utils.test.ts +++ b/packages/client-nuxt/src/__tests__/utils.test.ts @@ -1,57 +1,7 @@ +import type { Auth } from '@hey-api/client-core'; import { describe, expect, it, vi } from 'vitest'; -import type { Auth } from '../types'; -import { getAuthToken, mergeInterceptors, setAuthParams } from '../utils'; - -describe('getAuthToken', () => { - it('returns bearer token', async () => { - const auth = vi.fn().mockReturnValue('foo'); - const token = await getAuthToken( - { - scheme: 'bearer', - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe('Bearer foo'); - }); - - it('returns basic token', async () => { - const auth = vi.fn().mockReturnValue('foo:bar'); - const token = await getAuthToken( - { - scheme: 'basic', - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe(`Basic ${btoa('foo:bar')}`); - }); - - it('returns raw token', async () => { - const auth = vi.fn().mockReturnValue('foo'); - const token = await getAuthToken( - { - type: 'http', - }, - auth, - ); - expect(auth).toHaveBeenCalled(); - expect(token).toBe('foo'); - }); - - it('returns nothing when auth function is undefined', async () => { - const token = await getAuthToken( - { - type: 'http', - }, - undefined, - ); - expect(token).toBeUndefined(); - }); -}); +import { mergeInterceptors, setAuthParams } from '../utils'; describe('mergeInterceptors', () => { it('handles no arguments', () => { diff --git a/packages/client-nuxt/src/index.ts b/packages/client-nuxt/src/index.ts index f76332582..5557caee4 100644 --- a/packages/client-nuxt/src/index.ts +++ b/packages/client-nuxt/src/index.ts @@ -151,19 +151,18 @@ export const createClient = (config: Config = {}): Client => { }; export type { - Auth, Client, Composable, Config, Options, OptionsLegacyParser, - QuerySerializerOptions, RequestOptions, RequestResult, } from './types'; +export { createConfig } from './utils'; +export type { Auth, QuerySerializerOptions } from '@hey-api/client-core'; export { - createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, -} from './utils'; +} from '@hey-api/client-core'; diff --git a/packages/client-nuxt/src/types.ts b/packages/client-nuxt/src/types.ts index 0aff7480f..ffcd24348 100644 --- a/packages/client-nuxt/src/types.ts +++ b/packages/client-nuxt/src/types.ts @@ -1,3 +1,8 @@ +import type { + Auth, + BodySerializer, + QuerySerializerOptions, +} from '@hey-api/client-core'; import type { AsyncDataOptions, useAsyncData, @@ -14,26 +19,10 @@ type MatrixStyle = 'label' | 'matrix' | 'simple'; export type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; type ObjectStyle = 'form' | 'deepObject'; -export type BodySerializer = (body: any) => any; - -export interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; -} - export type QuerySerializer = ( query: Parameters[0]['query'], ) => string; -export interface QuerySerializerOptions { - allowReserved?: boolean; - array?: SerializerOptions; - object?: SerializerOptions; -} - type OmitKeys = Pick>; type WithRefs = { @@ -49,8 +38,6 @@ export interface Config >, WithRefs, 'query'>> { /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * * Auth token or a function returning auth token. The resolved value will be * added to the request payload as defined by its `security` array. */ @@ -113,8 +100,6 @@ export interface Config */ responseTransformer?: (data: unknown) => Promise; /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * * A function validating response data. This is useful if you want to ensure * the response conforms to the desired shape, so it can be safely passed to * the transformers and returned to the user. @@ -122,13 +107,6 @@ export interface Config responseValidator?: (data: unknown) => Promise; } -export interface Auth { - in?: 'header' | 'query'; - name?: string; - scheme?: 'basic' | 'bearer'; - type: 'apiKey' | 'http'; -} - type AuthToken = string | undefined; export interface RequestOptions< diff --git a/packages/client-nuxt/src/utils.ts b/packages/client-nuxt/src/utils.ts index 97b064d92..692c46e5e 100644 --- a/packages/client-nuxt/src/utils.ts +++ b/packages/client-nuxt/src/utils.ts @@ -1,17 +1,21 @@ +import type { QuerySerializerOptions } from '@hey-api/client-core'; +import { + getAuthToken, + jsonBodySerializer, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '@hey-api/client-core'; import type { ComputedRef, Ref } from 'vue'; import { isRef, toValue, unref } from 'vue'; import type { ArraySeparatorStyle, - Auth, BuildUrlOptions, Client, Config, - ObjectSeparatorStyle, QuerySerializer, - QuerySerializerOptions, RequestOptions, - SerializerOptions, } from './types'; type PathSerializer = Pick, 'path' | 'url'>; @@ -20,168 +24,6 @@ const PATH_PARAM_RE = /\{[^{}]+\}/g; type MaybeArray = T | T[]; -interface SerializeOptions - extends SerializePrimitiveOptions, - SerializerOptions {} -interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; -} -interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; -} - -const serializePrimitiveParam = ({ - allowReserved, - name, - value, -}: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ''; - } - - if (typeof value === 'object') { - throw new Error( - 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', - ); - } - - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; -}; - -const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; - default: - return ','; - } -}; - -const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: unknown[]; -}) => { - if (!explode) { - const joinedValues = ( - allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) - ).join(separatorArrayNoExplode(style)); - switch (style) { - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - case 'simple': - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } - - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === 'label' || style === 'simple') { - return allowReserved ? v : encodeURIComponent(v as string); - } - - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: Record | Date; -}) => { - if (value instanceof Date) { - return `${name}=${value.toISOString()}`; - } - - if (style !== 'deepObject' && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [ - ...values, - key, - allowReserved ? (v as string) : encodeURIComponent(v as string), - ]; - }); - const joinedValues = values.join(','); - switch (style) { - case 'form': - return `${name}=${joinedValues}`; - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } - - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { let url = _url; const matches = _url.match(PATH_PARAM_RE); @@ -312,28 +154,6 @@ export const createQuerySerializer = ({ return querySerializer; }; -export const getAuthToken = async ( - auth: Auth, - callback: RequestOptions['auth'], -): Promise => { - const token = - typeof callback === 'function' ? await callback(auth) : callback; - - if (!token) { - return; - } - - if (auth.scheme === 'bearer') { - return `Bearer ${token}`; - } - - if (auth.scheme === 'basic') { - return `Basic ${btoa(token)}`; - } - - return token; -}; - export const setAuthParams = async ({ security, ...options @@ -465,72 +285,6 @@ export const mergeInterceptors = (...args: Array>): Array => return acc; }, []); -const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { - if (typeof value === 'string' || value instanceof Blob) { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new FormData(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(data, key, v)); - } else { - serializeFormDataPair(data, key, value); - } - }); - - return data; - }, -}; - -export const jsonBodySerializer = { - bodySerializer: (body: T) => JSON.stringify(body), -}; - -const serializeUrlSearchParamsPair = ( - data: URLSearchParams, - key: string, - value: unknown, -) => { - if (typeof value === 'string') { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new URLSearchParams(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); - - return data; - }, -}; - const defaultQuerySerializer = createQuerySerializer({ allowReserved: false, array: { diff --git a/packages/openapi-ts/src/generate/client.ts b/packages/openapi-ts/src/generate/client.ts index 6b2f95ae3..fa80c3884 100644 --- a/packages/openapi-ts/src/generate/client.ts +++ b/packages/openapi-ts/src/generate/client.ts @@ -54,20 +54,17 @@ export const generateClientBundle = ({ const clientModulePath = path.normalize(require.resolve(name)); const clientModulePathComponents = clientModulePath.split(path.sep); - const clientSrcPath = [ - ...clientModulePathComponents.slice( - 0, - clientModulePathComponents.indexOf('dist'), - ), - 'src', - ].join(path.sep); + const clientDistPath = clientModulePathComponents + .slice(0, clientModulePathComponents.indexOf('dist') + 1) + .join(path.sep); - // copy client modules - const files = ['index.ts', 'types.ts', 'utils.ts']; - files.forEach((file) => { + const indexJsFile = + clientModulePathComponents[clientModulePathComponents.length - 1]; + const distFiles = [indexJsFile!, 'index.d.cts', 'index.d.ts']; + for (const file of distFiles) { fs.copyFileSync( - path.resolve(clientSrcPath, file), + path.resolve(clientDistPath, file), path.resolve(dirPath, file), ); - }); + } }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.cjs b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.cjs new file mode 100644 index 000000000..ae1cfe2b4 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.cjs @@ -0,0 +1,2 @@ +'use strict';var D=require('axios');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var D__default=/*#__PURE__*/_interopDefault(D);var R=async(t,e)=>{let r=typeof e=="function"?await e(t):e;if(r)return t.scheme==="bearer"?`Bearer ${r}`:t.scheme==="basic"?`Basic ${btoa(r)}`:r},w=(t,e,r)=>{typeof r=="string"||r instanceof Blob?t.append(e,r):t.append(e,JSON.stringify(r));},C=(t,e,r)=>{typeof r=="string"?t.append(e,r):t.append(e,JSON.stringify(r));},O={bodySerializer:t=>{let e=new FormData;return Object.entries(t).forEach(([r,i])=>{i!=null&&(Array.isArray(i)?i.forEach(a=>w(e,r,a)):w(e,r,i));}),e}},q={bodySerializer:t=>JSON.stringify(t)},v={bodySerializer:t=>{let e=new URLSearchParams;return Object.entries(t).forEach(([r,i])=>{i!=null&&(Array.isArray(i)?i.forEach(a=>C(e,r,a)):C(e,r,i));}),e}},$=t=>{switch(t){case "label":return ".";case "matrix":return ";";case "simple":return ",";default:return "&"}},P=t=>{switch(t){case "form":return ",";case "pipeDelimited":return "|";case "spaceDelimited":return "%20";default:return ","}},k=t=>{switch(t){case "label":return ".";case "matrix":return ";";case "simple":return ",";default:return "&"}},h=({allowReserved:t,explode:e,name:r,style:i,value:a})=>{if(!e){let n=(t?a:a.map(o=>encodeURIComponent(o))).join(P(i));switch(i){case "label":return `.${n}`;case "matrix":return `;${r}=${n}`;case "simple":return n;default:return `${r}=${n}`}}let s=$(i),l=a.map(n=>i==="label"||i==="simple"?t?n:encodeURIComponent(n):d({allowReserved:t,name:r,value:n})).join(s);return i==="label"||i==="matrix"?s+l:l},d=({allowReserved:t,name:e,value:r})=>{if(r==null)return "";if(typeof r=="object")throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");return `${e}=${t?r:encodeURIComponent(r)}`},g=({allowReserved:t,explode:e,name:r,style:i,value:a})=>{if(a instanceof Date)return `${r}=${a.toISOString()}`;if(i!=="deepObject"&&!e){let n=[];Object.entries(a).forEach(([u,f])=>{n=[...n,u,t?f:encodeURIComponent(f)];});let o=n.join(",");switch(i){case "form":return `${r}=${o}`;case "label":return `.${o}`;case "matrix":return `;${r}=${o}`;default:return o}}let s=k(i),l=Object.entries(a).map(([n,o])=>d({allowReserved:t,name:i==="deepObject"?`${r}[${n}]`:n,value:o})).join(s);return i==="label"||i==="matrix"?s+l:l};var E=/\{[^{}]+\}/g,U=({path:t,url:e})=>{let r=e,i=e.match(E);if(i)for(let a of i){let s=false,l=a.substring(1,a.length-1),n="simple";l.endsWith("*")&&(s=true,l=l.substring(0,l.length-1)),l.startsWith(".")?(l=l.substring(1),n="label"):l.startsWith(";")&&(l=l.substring(1),n="matrix");let o=t[l];if(o==null)continue;if(Array.isArray(o)){r=r.replace(a,h({explode:s,name:l,style:n,value:o}));continue}if(typeof o=="object"){r=r.replace(a,g({explode:s,name:l,style:n,value:o}));continue}if(n==="matrix"){r=r.replace(a,`;${d({name:l,value:o})}`);continue}let u=encodeURIComponent(n==="label"?`.${o}`:o);r=r.replace(a,u);}return r},B=({allowReserved:t,array:e,object:r}={})=>a=>{let s=[];if(a&&typeof a=="object")for(let l in a){let n=a[l];if(n!=null){if(Array.isArray(n)){s=[...s,h({allowReserved:t,explode:true,name:l,style:"form",value:n,...e})];continue}if(typeof n=="object"){s=[...s,g({allowReserved:t,explode:true,name:l,style:"deepObject",value:n,...r})];continue}s=[...s,d({allowReserved:t,name:l,value:n})];}}return s.join("&")},A=async({security:t,...e})=>{for(let r of t){let i=await R(r,e.auth);if(!i)continue;let a=r.name??"Authorization";switch(r.in){case "query":e.query||(e.query={}),e.query[a]=i;break;case "header":default:e.headers[a]=i;break}return}},b=t=>H({path:t.path,query:t.paramsSerializer?undefined:t.query,querySerializer:typeof t.querySerializer=="function"?t.querySerializer:B(t.querySerializer),url:t.url}),H=({path:t,query:e,querySerializer:r,url:i})=>{let s=i.startsWith("/")?i:`/${i}`;t&&(s=U({path:t,url:s}));let l=e?r(e):"";return l.startsWith("?")&&(l=l.substring(1)),l&&(s+=`?${l}`),s},x=(t,e)=>{let r={...t,...e};return r.headers=y(t.headers,e.headers),r},T=["common","delete","get","head","patch","post","put"],y=(...t)=>{let e={};for(let r of t){if(!r||typeof r!="object")continue;let i=Object.entries(r);for(let[a,s]of i)if(T.includes(a)&&typeof s=="object")e[a]={...e[a],...s};else if(s===null)delete e[a];else if(Array.isArray(s))for(let l of s)e[a]=[...e[a]??[],l];else s!==undefined&&(e[a]=typeof s=="object"?JSON.stringify(s):s);}return e},S=(t={})=>({baseURL:"",...t});var L=t=>{let e=x(S(),t),{auth:r,...i}=e,a=D__default.default.create(i),s=()=>({...e}),l=o=>(e=x(e,o),a.defaults={...a.defaults,...e,headers:y(a.defaults.headers,e.headers)},s()),n=async o=>{let u={...e,...o,axios:o.axios??e.axios??a,headers:y(e.headers,o.headers)};u.security&&await A({...u,security:u.security}),u.body&&u.bodySerializer&&(u.body=u.bodySerializer(u.body));let f=b(u);try{let m=u.axios,{auth:c,...j}=u,z=await m({...j,data:u.body,headers:u.headers,params:u.paramsSerializer?u.query:void 0,url:f}),{data:p}=z;return u.responseType==="json"&&(u.responseValidator&&await u.responseValidator(p),u.responseTransformer&&(p=await u.responseTransformer(p))),{...z,data:p??{}}}catch(m){let c=m;if(u.throwOnError)throw c;return c.error=c.response?.data??{},c}};return {buildUrl:b,delete:o=>n({...o,method:"delete"}),get:o=>n({...o,method:"get"}),getConfig:s,head:o=>n({...o,method:"head"}),instance:a,options:o=>n({...o,method:"options"}),patch:o=>n({...o,method:"patch"}),post:o=>n({...o,method:"post"}),put:o=>n({...o,method:"put"}),request:n,setConfig:l}};exports.createClient=L;exports.createConfig=S;exports.formDataBodySerializer=O;exports.jsonBodySerializer=q;exports.urlSearchParamsBodySerializer=v;//# sourceMappingURL=index.cjs.map +//# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.d.cts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.d.cts new file mode 100644 index 000000000..3d79cc27d --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.d.cts @@ -0,0 +1,169 @@ +import { CreateAxiosDefaults, AxiosStatic, AxiosResponse, AxiosError, AxiosInstance } from 'axios'; + +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} +interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type ObjectStyle = 'form' | 'deepObject'; + +type QuerySerializer = (query: Record) => string; +type BodySerializer = (body: any) => any; +interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} +declare const formDataBodySerializer: { + bodySerializer: | Array>>(body: T) => FormData; +}; +declare const jsonBodySerializer: { + bodySerializer: (body: T) => string; +}; +declare const urlSearchParamsBodySerializer: { + bodySerializer: | Array>>(body: T) => URLSearchParams; +}; + +type OmitKeys = Pick>; +interface Config extends Omit { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * Axios implementation. You can use this option to provide a custom + * Axios instance. + * + * @default axios + */ + axios?: AxiosStatic; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: CreateAxiosDefaults['headers'] | Record; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: 'connect' | 'delete' | 'get' | 'head' | 'options' | 'patch' | 'post' | 'put' | 'trace'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: ThrowOnError; +} +type AuthToken = string | undefined; +interface RequestOptions extends Config { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} +type RequestResult = ThrowOnError extends true ? Promise> : Promise<(AxiosResponse & { + error: undefined; +}) | (AxiosError & { + data: undefined; + error: TError; +})>; +type MethodFn = (options: Omit, 'method'>) => RequestResult; +type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; +interface Client { + /** + * Returns the final request URL. This method works only with experimental parser. + */ + buildUrl: ; + query?: Record; + url: string; + }>(options: Pick & Omit, 'axios'>) => string; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + instance: AxiosInstance; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; +} +interface DataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} +type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & Omit; +type OptionsLegacyParser = 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; + +declare const createConfig: (override?: Config) => Config; + +declare const createClient: (config: Config) => Client; + +export { type Auth, type Client, type Config, type Options, type OptionsLegacyParser, type QuerySerializerOptions, type RequestOptions, type RequestResult, createClient, createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.d.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.d.ts new file mode 100644 index 000000000..3d79cc27d --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.d.ts @@ -0,0 +1,169 @@ +import { CreateAxiosDefaults, AxiosStatic, AxiosResponse, AxiosError, AxiosInstance } from 'axios'; + +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} +interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type ObjectStyle = 'form' | 'deepObject'; + +type QuerySerializer = (query: Record) => string; +type BodySerializer = (body: any) => any; +interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} +declare const formDataBodySerializer: { + bodySerializer: | Array>>(body: T) => FormData; +}; +declare const jsonBodySerializer: { + bodySerializer: (body: T) => string; +}; +declare const urlSearchParamsBodySerializer: { + bodySerializer: | Array>>(body: T) => URLSearchParams; +}; + +type OmitKeys = Pick>; +interface Config extends Omit { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * Axios implementation. You can use this option to provide a custom + * Axios instance. + * + * @default axios + */ + axios?: AxiosStatic; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: CreateAxiosDefaults['headers'] | Record; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: 'connect' | 'delete' | 'get' | 'head' | 'options' | 'patch' | 'post' | 'put' | 'trace'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: ThrowOnError; +} +type AuthToken = string | undefined; +interface RequestOptions extends Config { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} +type RequestResult = ThrowOnError extends true ? Promise> : Promise<(AxiosResponse & { + error: undefined; +}) | (AxiosError & { + data: undefined; + error: TError; +})>; +type MethodFn = (options: Omit, 'method'>) => RequestResult; +type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; +interface Client { + /** + * Returns the final request URL. This method works only with experimental parser. + */ + buildUrl: ; + query?: Record; + url: string; + }>(options: Pick & Omit, 'axios'>) => string; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + instance: AxiosInstance; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; +} +interface DataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} +type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & Omit; +type OptionsLegacyParser = 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; + +declare const createConfig: (override?: Config) => Config; + +declare const createClient: (config: Config) => Client; + +export { type Auth, type Client, type Config, type Options, type OptionsLegacyParser, type QuerySerializerOptions, type RequestOptions, type RequestResult, createClient, createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.ts deleted file mode 100644 index c13193000..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/index.ts +++ /dev/null @@ -1,126 +0,0 @@ -import type { AxiosError, RawAxiosRequestHeaders } from 'axios'; -import axios from 'axios'; - -import type { Client, Config } from './types'; -import { - buildUrl, - createConfig, - mergeConfigs, - mergeHeaders, - setAuthParams, -} from './utils'; - -export const createClient = (config: Config): Client => { - let _config = mergeConfigs(createConfig(), config); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { auth, ...configWithoutAuth } = _config; - const instance = axios.create(configWithoutAuth); - - const getConfig = (): Config => ({ ..._config }); - - const setConfig = (config: Config): Config => { - _config = mergeConfigs(_config, config); - instance.defaults = { - ...instance.defaults, - ..._config, - // @ts-expect-error - headers: mergeHeaders(instance.defaults.headers, _config.headers), - }; - return getConfig(); - }; - - // @ts-expect-error - const request: Client['request'] = async (options) => { - const opts = { - ..._config, - ...options, - axios: options.axios ?? _config.axios ?? instance, - headers: mergeHeaders(_config.headers, options.headers), - }; - - if (opts.security) { - await setAuthParams({ - ...opts, - security: opts.security, - }); - } - - if (opts.body && opts.bodySerializer) { - opts.body = opts.bodySerializer(opts.body); - } - - const url = buildUrl(opts); - - 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({ - ...optsWithoutAuth, - data: opts.body, - headers: opts.headers as RawAxiosRequestHeaders, - // let `paramsSerializer()` handle query params if it exists - params: opts.paramsSerializer ? opts.query : undefined, - url, - }); - - let { data } = response; - - if (opts.responseType === 'json') { - if (opts.responseValidator) { - await opts.responseValidator(data); - } - - if (opts.responseTransformer) { - data = await opts.responseTransformer(data); - } - } - - return { - ...response, - data: data ?? {}, - }; - } catch (error) { - const e = error as AxiosError; - if (opts.throwOnError) { - throw e; - } - // @ts-expect-error - e.error = e.response?.data ?? {}; - return e; - } - }; - - return { - buildUrl, - delete: (options) => request({ ...options, method: 'delete' }), - get: (options) => request({ ...options, method: 'get' }), - getConfig, - head: (options) => request({ ...options, method: 'head' }), - instance, - options: (options) => request({ ...options, method: 'options' }), - patch: (options) => request({ ...options, method: 'patch' }), - post: (options) => request({ ...options, method: 'post' }), - put: (options) => request({ ...options, method: 'put' }), - request, - setConfig, - } as Client; -}; - -export type { - Auth, - Client, - Config, - Options, - OptionsLegacyParser, - RequestOptions, - RequestResult, -} from './types'; -export { - createConfig, - formDataBodySerializer, - jsonBodySerializer, - urlSearchParamsBodySerializer, -} from './utils'; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/types.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/types.ts deleted file mode 100644 index 501a4a291..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/types.ts +++ /dev/null @@ -1,215 +0,0 @@ -import type { - AxiosError, - AxiosInstance, - AxiosResponse, - AxiosStatic, - CreateAxiosDefaults, -} from 'axios'; - -import type { - BodySerializer, - QuerySerializer, - QuerySerializerOptions, -} from './utils'; - -type OmitKeys = Pick>; - -export interface Config - extends Omit { - /** - * Auth token or a function returning auth token. The resolved value will be - * added to the request payload as defined by its `security` array. - */ - auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; - /** - * Axios implementation. You can use this option to provide a custom - * Axios instance. - * - * @default axios - */ - axios?: AxiosStatic; - /** - * A function for serializing request body parameter. By default, - * {@link JSON.stringify()} will be used. - */ - bodySerializer?: BodySerializer | null; - /** - * An object containing any HTTP headers that you want to pre-populate your - * `Headers` object with. - * - * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} - */ - headers?: - | CreateAxiosDefaults['headers'] - | Record< - string, - | string - | number - | boolean - | (string | number | boolean)[] - | null - | undefined - | unknown - >; - /** - * The request method. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} - */ - method?: - | 'connect' - | 'delete' - | 'get' - | 'head' - | 'options' - | 'patch' - | 'post' - | 'put' - | 'trace'; - /** - * A function for serializing request query parameters. By default, arrays - * will be exploded in form style, objects will be exploded in deepObject - * style, and reserved characters are percent-encoded. - * - * This method will have no effect if the native `paramsSerializer()` Axios - * API function is used. - * - * {@link https://swagger.io/docs/specification/serialization/#query View examples} - */ - querySerializer?: QuerySerializer | QuerySerializerOptions; - /** - * A function transforming response data before it's returned. This is useful - * for post-processing data, e.g. converting ISO strings into Date objects. - */ - responseTransformer?: (data: unknown) => Promise; - /** - * A function validating response data. This is useful if you want to ensure - * the response conforms to the desired shape, so it can be safely passed to - * the transformers and returned to the user. - */ - responseValidator?: (data: unknown) => Promise; - /** - * Throw an error instead of returning it in the response? - * - * @default false - */ - 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, -> extends Config { - /** - * Any body that you want to add to your request. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} - */ - body?: unknown; - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; - path?: Record; - query?: Record; - /** - * Security mechanism(s) to use for the request. - */ - security?: ReadonlyArray; - url: Url; -} - -export type RequestResult< - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = boolean, -> = ThrowOnError extends true - ? Promise> - : Promise< - | (AxiosResponse & { error: undefined }) - | (AxiosError & { data: undefined; error: TError }) - >; - -type MethodFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, ->( - options: Omit, 'method'>, -) => RequestResult; - -type RequestFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, ->( - options: Omit, 'method'> & - Pick>, 'method'>, -) => RequestResult; - -export interface Client { - /** - * Returns the final request URL. This method works only with experimental parser. - */ - buildUrl: < - TData extends { - body?: unknown; - path?: Record; - query?: Record; - url: string; - }, - >( - options: Pick & Omit, 'axios'>, - ) => string; - delete: MethodFn; - get: MethodFn; - getConfig: () => Config; - head: MethodFn; - instance: AxiosInstance; - options: MethodFn; - patch: MethodFn; - post: MethodFn; - put: MethodFn; - request: RequestFn; - setConfig: (config: Config) => Config; -} - -interface DataShape { - body?: unknown; - headers?: unknown; - path?: unknown; - query?: unknown; - url: string; -} - -export type Options< - TData extends DataShape = DataShape, - ThrowOnError extends boolean = boolean, -> = 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; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/utils.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/utils.ts deleted file mode 100644 index e8fefad23..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/bundle/client/utils.ts +++ /dev/null @@ -1,548 +0,0 @@ -import type { Auth, Client, Config, RequestOptions } from './types'; - -interface PathSerializer { - path: Record; - url: string; -} - -const PATH_PARAM_RE = /\{[^{}]+\}/g; - -type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; -type MatrixStyle = 'label' | 'matrix' | 'simple'; -type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type ObjectStyle = 'form' | 'deepObject'; -type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; - -export type QuerySerializer = (query: Record) => string; - -export type BodySerializer = (body: any) => any; - -interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; -} - -interface SerializeOptions - extends SerializePrimitiveOptions, - SerializerOptions {} -interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; -} -interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; -} - -export interface QuerySerializerOptions { - allowReserved?: boolean; - array?: SerializerOptions; - object?: SerializerOptions; -} - -const serializePrimitiveParam = ({ - allowReserved, - name, - value, -}: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ''; - } - - if (typeof value === 'object') { - throw new Error( - 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', - ); - } - - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; -}; - -const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; - default: - return ','; - } -}; - -const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: unknown[]; -}) => { - if (!explode) { - const joinedValues = ( - allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) - ).join(separatorArrayNoExplode(style)); - switch (style) { - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - case 'simple': - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } - - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === 'label' || style === 'simple') { - return allowReserved ? v : encodeURIComponent(v as string); - } - - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: Record; -}) => { - if (style !== 'deepObject' && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [ - ...values, - key, - allowReserved ? (v as string) : encodeURIComponent(v as string), - ]; - }); - const joinedValues = values.join(','); - switch (style) { - case 'form': - return `${name}=${joinedValues}`; - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } - - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { - let url = _url; - const matches = _url.match(PATH_PARAM_RE); - if (matches) { - for (const match of matches) { - let explode = false; - let name = match.substring(1, match.length - 1); - let style: ArraySeparatorStyle = 'simple'; - - if (name.endsWith('*')) { - explode = true; - name = name.substring(0, name.length - 1); - } - - if (name.startsWith('.')) { - name = name.substring(1); - style = 'label'; - } else if (name.startsWith(';')) { - name = name.substring(1); - style = 'matrix'; - } - - const value = path[name]; - - if (value === undefined || value === null) { - continue; - } - - if (Array.isArray(value)) { - url = url.replace( - match, - serializeArrayParam({ explode, name, style, value }), - ); - continue; - } - - if (typeof value === 'object') { - url = url.replace( - match, - serializeObjectParam({ - explode, - name, - style, - value: value as Record, - }), - ); - continue; - } - - if (style === 'matrix') { - url = url.replace( - match, - `;${serializePrimitiveParam({ - name, - value: value as string, - })}`, - ); - continue; - } - - const replaceValue = encodeURIComponent( - style === 'label' ? `.${value as string}` : (value as string), - ); - url = url.replace(match, replaceValue); - } - } - return url; -}; - -export const createQuerySerializer = ({ - allowReserved, - array, - object, -}: QuerySerializerOptions = {}) => { - const querySerializer = (queryParams: T) => { - let search: string[] = []; - if (queryParams && typeof queryParams === 'object') { - for (const name in queryParams) { - const value = queryParams[name]; - - if (value === undefined || value === null) { - continue; - } - - if (Array.isArray(value)) { - search = [ - ...search, - serializeArrayParam({ - allowReserved, - explode: true, - name, - style: 'form', - value, - ...array, - }), - ]; - continue; - } - - if (typeof value === 'object') { - search = [ - ...search, - serializeObjectParam({ - allowReserved, - explode: true, - name, - style: 'deepObject', - value: value as Record, - ...object, - }), - ]; - continue; - } - - search = [ - ...search, - serializePrimitiveParam({ - allowReserved, - name, - value: value as string, - }), - ]; - } - } - return search.join('&'); - }; - return querySerializer; -}; - -export const getAuthToken = async ( - auth: Auth, - callback: RequestOptions['auth'], -): Promise => { - const token = - typeof callback === 'function' ? await callback(auth) : callback; - - if (!token) { - return; - } - - if (auth.scheme === 'bearer') { - return `Bearer ${token}`; - } - - if (auth.scheme === 'basic') { - return `Basic ${btoa(token)}`; - } - - return token; -}; - -export const setAuthParams = async ({ - security, - ...options -}: Pick, 'security'> & - Pick & { - headers: Record; - }) => { - for (const auth of security) { - const token = await getAuthToken(auth, options.auth); - - if (!token) { - continue; - } - - const name = auth.name ?? 'Authorization'; - - switch (auth.in) { - case 'query': - if (!options.query) { - options.query = {}; - } - options.query[name] = token; - break; - case 'header': - default: - options.headers[name] = token; - break; - } - - return; - } -}; - -export const buildUrl: Client['buildUrl'] = (options) => { - const url = getUrl({ - path: options.path, - // let `paramsSerializer()` handle query params if it exists - query: !options.paramsSerializer ? options.query : undefined, - querySerializer: - typeof options.querySerializer === 'function' - ? options.querySerializer - : createQuerySerializer(options.querySerializer), - url: options.url, - }); - return url; -}; - -export const getUrl = ({ - path, - query, - querySerializer, - url: _url, -}: { - path?: Record; - query?: Record; - querySerializer: QuerySerializer; - url: string; -}) => { - const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; - let url = pathUrl; - if (path) { - url = defaultPathSerializer({ path, url }); - } - let search = query ? querySerializer(query) : ''; - if (search.startsWith('?')) { - search = search.substring(1); - } - if (search) { - url += `?${search}`; - } - return url; -}; - -const serializeFormDataPair = ( - formData: FormData, - key: string, - value: unknown, -) => { - if (typeof value === 'string' || value instanceof Blob) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } -}; - -export const mergeConfigs = (a: Config, b: Config): Config => { - const config = { ...a, ...b }; - config.headers = mergeHeaders(a.headers, b.headers); - return config; -}; - -/** - * Special Axios headers keywords allowing to set headers by request method. - */ -export const axiosHeadersKeywords = [ - 'common', - 'delete', - 'get', - 'head', - 'patch', - 'post', - 'put', -] as const; - -export const mergeHeaders = ( - ...headers: Array['headers'] | undefined> -): Record => { - const mergedHeaders: Record = {}; - for (const header of headers) { - if (!header || typeof header !== 'object') { - continue; - } - - const iterator = Object.entries(header); - - for (const [key, value] of iterator) { - if ( - axiosHeadersKeywords.includes( - key as (typeof axiosHeadersKeywords)[number], - ) && - typeof value === 'object' - ) { - mergedHeaders[key] = { - ...(mergedHeaders[key] as Record), - ...value, - }; - } else if (value === null) { - delete mergedHeaders[key]; - } else if (Array.isArray(value)) { - for (const v of value) { - // @ts-expect-error - mergedHeaders[key] = [...(mergedHeaders[key] ?? []), v as string]; - } - } else if (value !== undefined) { - // 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); - } - } - } - return mergedHeaders; -}; - -export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const formData = new FormData(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(formData, key, v)); - } else { - serializeFormDataPair(formData, key, value); - } - }); - - return formData; - }, -}; - -export const jsonBodySerializer = { - bodySerializer: (body: T) => JSON.stringify(body), -}; - -const serializeUrlSearchParamsPair = ( - data: URLSearchParams, - key: string, - value: unknown, -) => { - if (typeof value === 'string') { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new URLSearchParams(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); - - return data; - }, -}; - -export const createConfig = (override: Config = {}): Config => ({ - baseURL: '', - ...override, -}); diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.cjs b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.cjs new file mode 100644 index 000000000..b614e5c14 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.cjs @@ -0,0 +1,2 @@ +'use strict';var A=async(t,r)=>{let e=typeof r=="function"?await r(t):r;if(e)return t.scheme==="bearer"?`Bearer ${e}`:t.scheme==="basic"?`Basic ${btoa(e)}`:e},j=(t,r,e)=>{typeof e=="string"||e instanceof Blob?t.append(r,e):t.append(r,JSON.stringify(e));},z=(t,r,e)=>{typeof e=="string"?t.append(r,e):t.append(r,JSON.stringify(e));},U={bodySerializer:t=>{let r=new FormData;return Object.entries(t).forEach(([e,i])=>{i!=null&&(Array.isArray(i)?i.forEach(a=>j(r,e,a)):j(r,e,i));}),r}},b={bodySerializer:t=>JSON.stringify(t)},k={bodySerializer:t=>{let r=new URLSearchParams;return Object.entries(t).forEach(([e,i])=>{i!=null&&(Array.isArray(i)?i.forEach(a=>z(r,e,a)):z(r,e,i));}),r}},T=t=>{switch(t){case "label":return ".";case "matrix":return ";";case "simple":return ",";default:return "&"}},_=t=>{switch(t){case "form":return ",";case "pipeDelimited":return "|";case "spaceDelimited":return "%20";default:return ","}},D=t=>{switch(t){case "label":return ".";case "matrix":return ";";case "simple":return ",";default:return "&"}},q=({allowReserved:t,explode:r,name:e,style:i,value:a})=>{if(!r){let s=(t?a:a.map(l=>encodeURIComponent(l))).join(_(i));switch(i){case "label":return `.${s}`;case "matrix":return `;${e}=${s}`;case "simple":return s;default:return `${e}=${s}`}}let o=T(i),n=a.map(s=>i==="label"||i==="simple"?t?s:encodeURIComponent(s):y({allowReserved:t,name:e,value:s})).join(o);return i==="label"||i==="matrix"?o+n:n},y=({allowReserved:t,name:r,value:e})=>{if(e==null)return "";if(typeof e=="object")throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");return `${r}=${t?e:encodeURIComponent(e)}`},O=({allowReserved:t,explode:r,name:e,style:i,value:a})=>{if(a instanceof Date)return `${e}=${a.toISOString()}`;if(i!=="deepObject"&&!r){let s=[];Object.entries(a).forEach(([f,u])=>{s=[...s,f,t?u:encodeURIComponent(u)];});let l=s.join(",");switch(i){case "form":return `${e}=${l}`;case "label":return `.${l}`;case "matrix":return `;${e}=${l}`;default:return l}}let o=D(i),n=Object.entries(a).map(([s,l])=>y({allowReserved:t,name:i==="deepObject"?`${e}[${s}]`:s,value:l})).join(o);return i==="label"||i==="matrix"?o+n:n};var H=/\{[^{}]+\}/g,B=({path:t,url:r})=>{let e=r,i=r.match(H);if(i)for(let a of i){let o=false,n=a.substring(1,a.length-1),s="simple";n.endsWith("*")&&(o=true,n=n.substring(0,n.length-1)),n.startsWith(".")?(n=n.substring(1),s="label"):n.startsWith(";")&&(n=n.substring(1),s="matrix");let l=t[n];if(l==null)continue;if(Array.isArray(l)){e=e.replace(a,q({explode:o,name:n,style:s,value:l}));continue}if(typeof l=="object"){e=e.replace(a,O({explode:o,name:n,style:s,value:l}));continue}if(s==="matrix"){e=e.replace(a,`;${y({name:n,value:l})}`);continue}let f=encodeURIComponent(s==="label"?`.${l}`:l);e=e.replace(a,f);}return e},E=({allowReserved:t,array:r,object:e}={})=>a=>{let o=[];if(a&&typeof a=="object")for(let n in a){let s=a[n];if(s!=null){if(Array.isArray(s)){o=[...o,q({allowReserved:t,explode:true,name:n,style:"form",value:s,...r})];continue}if(typeof s=="object"){o=[...o,O({allowReserved:t,explode:true,name:n,style:"deepObject",value:s,...e})];continue}o=[...o,y({allowReserved:t,name:n,value:s})];}}return o.join("&")},P=t=>{if(!t)return "stream";let r=t.split(";")[0]?.trim();if(r){if(r.startsWith("application/json")||r.endsWith("+json"))return "json";if(r==="multipart/form-data")return "formData";if(["application/","audio/","image/","video/"].some(e=>r.startsWith(e)))return "blob";if(r.startsWith("text/"))return "text"}},I=async({security:t,...r})=>{for(let e of t){let i=await A(e,r.auth);if(!i)continue;let a=e.name??"Authorization";switch(e.in){case "query":r.query||(r.query={}),r.query[a]=i;break;case "header":default:r.headers.set(a,i);break}return}},S=t=>W({baseUrl:t.baseUrl??"",path:t.path,query:t.query,querySerializer:typeof t.querySerializer=="function"?t.querySerializer:E(t.querySerializer),url:t.url}),W=({baseUrl:t,path:r,query:e,querySerializer:i,url:a})=>{let o=a.startsWith("/")?a:`/${a}`,n=t+o;r&&(n=B({path:r,url:n}));let s=e?i(e):"";return s.startsWith("?")&&(s=s.substring(1)),s&&(n+=`?${s}`),n},x=(t,r)=>{let e={...t,...r};return e.baseUrl?.endsWith("/")&&(e.baseUrl=e.baseUrl.substring(0,e.baseUrl.length-1)),e.headers=w(t.headers,r.headers),e},w=(...t)=>{let r=new Headers;for(let e of t){if(!e||typeof e!="object")continue;let i=e instanceof Headers?e.entries():Object.entries(e);for(let[a,o]of i)if(o===null)r.delete(a);else if(Array.isArray(o))for(let n of o)r.append(a,n);else o!==undefined&&r.set(a,typeof o=="object"?JSON.stringify(o):o);}return r},h=class{_fns;constructor(){this._fns=[];}clear(){this._fns=[];}exists(r){return this._fns.indexOf(r)!==-1}eject(r){let e=this._fns.indexOf(r);e!==-1&&(this._fns=[...this._fns.slice(0,e),...this._fns.slice(e+1)]);}use(r){this._fns=[...this._fns,r];}},v=()=>({error:new h,request:new h,response:new h}),N=E({allowReserved:false,array:{explode:true,style:"form"},object:{explode:true,style:"deepObject"}}),Q={"Content-Type":"application/json"},C=(t={})=>({...b,baseUrl:"",headers:Q,parseAs:"auto",querySerializer:N,...t});var F=(t={})=>{let r=x(C(),t),e=()=>({...r}),i=n=>(r=x(r,n),e()),a=v(),o=async n=>{let s={...r,...n,fetch:n.fetch??r.fetch??globalThis.fetch,headers:w(r.headers,n.headers)};s.security&&await I({...s,security:s.security}),s.body&&s.bodySerializer&&(s.body=s.bodySerializer(s.body)),s.body||s.headers.delete("Content-Type");let l=S(s),f={redirect:"follow",...s},u=new Request(l,f);for(let p of a.request._fns)u=await p(u,s);let $=s.fetch,c=await $(u);for(let p of a.response._fns)c=await p(c,u,s);let m={request:u,response:c};if(c.ok){if(c.status===204||c.headers.get("Content-Length")==="0")return {data:{},...m};let p=(s.parseAs==="auto"?P(c.headers.get("Content-Type")):s.parseAs)??"json";if(p==="stream")return {data:c.body,...m};let R=await c[p]();return p==="json"&&(s.responseValidator&&await s.responseValidator(R),s.responseTransformer&&(R=await s.responseTransformer(R))),{data:R,...m}}let g=await c.text();try{g=JSON.parse(g);}catch{}let d=g;for(let p of a.error._fns)d=await p(g,c,u,s);if(d=d||{},s.throwOnError)throw d;return {error:d,...m}};return {buildUrl:S,connect:n=>o({...n,method:"CONNECT"}),delete:n=>o({...n,method:"DELETE"}),get:n=>o({...n,method:"GET"}),getConfig:e,head:n=>o({...n,method:"HEAD"}),interceptors:a,options:n=>o({...n,method:"OPTIONS"}),patch:n=>o({...n,method:"PATCH"}),post:n=>o({...n,method:"POST"}),put:n=>o({...n,method:"PUT"}),request:o,setConfig:i,trace:n=>o({...n,method:"TRACE"})}};exports.createClient=F;exports.createConfig=C;exports.formDataBodySerializer=U;exports.jsonBodySerializer=b;exports.urlSearchParamsBodySerializer=k;//# sourceMappingURL=index.cjs.map +//# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.d.cts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.d.cts new file mode 100644 index 000000000..03714c3b1 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.d.cts @@ -0,0 +1,205 @@ +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} +interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type ObjectStyle = 'form' | 'deepObject'; + +type QuerySerializer = (query: Record) => string; +type BodySerializer = (body: any) => any; +interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} +declare const formDataBodySerializer: { + bodySerializer: | Array>>(body: T) => FormData; +}; +declare const jsonBodySerializer: { + bodySerializer: (body: T) => string; +}; +declare const urlSearchParamsBodySerializer: { + bodySerializer: | Array>>(body: T) => URLSearchParams; +}; + +type ErrInterceptor = (error: Err, response: Res, request: Req, options: Options) => Err | Promise; +type ReqInterceptor = (request: Req, options: Options) => Req | Promise; +type ResInterceptor = (response: Res, request: Req, options: Options) => Res | Promise; +declare class Interceptors { + _fns: Interceptor[]; + constructor(); + clear(): void; + exists(fn: Interceptor): boolean; + eject(fn: Interceptor): void; + use(fn: Interceptor): void; +} +interface Middleware { + error: Pick>, 'eject' | 'use'>; + request: Pick>, 'eject' | 'use'>; + response: Pick>, 'eject' | 'use'>; +} +declare const createConfig: (override?: Config) => Config; + +type OmitKeys = Pick>; +interface Config extends Omit { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * Base URL for all requests made by this client. + * + * @default '' + */ + baseUrl?: string; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: (request: Request) => ReturnType; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: RequestInit['headers'] | Record; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: 'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE'; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: Exclude | 'auto' | 'stream'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: ThrowOnError; +} +type AuthToken = string | undefined; +interface RequestOptions extends Config { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: RequestInit['body'] | Record | Array> | Array | number; + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} +type RequestResult = ThrowOnError extends true ? Promise<{ + data: TData; + request: Request; + response: Response; +}> : Promise<({ + data: TData; + error: undefined; +} | { + data: undefined; + error: TError; +}) & { + request: Request; + response: Response; +}>; +type MethodFn = (options: Omit, 'method'>) => RequestResult; +type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; +interface Client { + /** + * Returns the final request URL. This method works only with experimental parser. + */ + buildUrl: ; + query?: Record; + url: string; + }>(options: Pick & Options) => string; + connect: MethodFn; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + interceptors: Middleware; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; + trace: MethodFn; +} +interface DataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} +type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & Omit; +type OptionsLegacyParser = 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; + +declare const createClient: (config?: Config) => Client; + +export { type Auth, type Client, type Config, type Options, type OptionsLegacyParser, type QuerySerializerOptions, type RequestOptions, type RequestResult, createClient, createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.d.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.d.ts new file mode 100644 index 000000000..03714c3b1 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.d.ts @@ -0,0 +1,205 @@ +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} +interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type ObjectStyle = 'form' | 'deepObject'; + +type QuerySerializer = (query: Record) => string; +type BodySerializer = (body: any) => any; +interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} +declare const formDataBodySerializer: { + bodySerializer: | Array>>(body: T) => FormData; +}; +declare const jsonBodySerializer: { + bodySerializer: (body: T) => string; +}; +declare const urlSearchParamsBodySerializer: { + bodySerializer: | Array>>(body: T) => URLSearchParams; +}; + +type ErrInterceptor = (error: Err, response: Res, request: Req, options: Options) => Err | Promise; +type ReqInterceptor = (request: Req, options: Options) => Req | Promise; +type ResInterceptor = (response: Res, request: Req, options: Options) => Res | Promise; +declare class Interceptors { + _fns: Interceptor[]; + constructor(); + clear(): void; + exists(fn: Interceptor): boolean; + eject(fn: Interceptor): void; + use(fn: Interceptor): void; +} +interface Middleware { + error: Pick>, 'eject' | 'use'>; + request: Pick>, 'eject' | 'use'>; + response: Pick>, 'eject' | 'use'>; +} +declare const createConfig: (override?: Config) => Config; + +type OmitKeys = Pick>; +interface Config extends Omit { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * Base URL for all requests made by this client. + * + * @default '' + */ + baseUrl?: string; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: (request: Request) => ReturnType; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: RequestInit['headers'] | Record; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: 'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE'; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: Exclude | 'auto' | 'stream'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: ThrowOnError; +} +type AuthToken = string | undefined; +interface RequestOptions extends Config { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: RequestInit['body'] | Record | Array> | Array | number; + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} +type RequestResult = ThrowOnError extends true ? Promise<{ + data: TData; + request: Request; + response: Response; +}> : Promise<({ + data: TData; + error: undefined; +} | { + data: undefined; + error: TError; +}) & { + request: Request; + response: Response; +}>; +type MethodFn = (options: Omit, 'method'>) => RequestResult; +type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; +interface Client { + /** + * Returns the final request URL. This method works only with experimental parser. + */ + buildUrl: ; + query?: Record; + url: string; + }>(options: Pick & Options) => string; + connect: MethodFn; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + interceptors: Middleware; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; + trace: MethodFn; +} +interface DataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} +type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & Omit; +type OptionsLegacyParser = 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; + +declare const createClient: (config?: Config) => Client; + +export { type Auth, type Client, type Config, type Options, type OptionsLegacyParser, type QuerySerializerOptions, type RequestOptions, type RequestResult, createClient, createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.ts deleted file mode 100644 index 22f8ab618..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/index.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { Client, Config, RequestOptions } from './types'; -import { - buildUrl, - createConfig, - createInterceptors, - getParseAs, - mergeConfigs, - mergeHeaders, - setAuthParams, -} from './utils'; - -type ReqInit = Omit & { - body?: any; - headers: ReturnType; -}; - -export const createClient = (config: Config = {}): Client => { - let _config = mergeConfigs(createConfig(), config); - - const getConfig = (): Config => ({ ..._config }); - - const setConfig = (config: Config): Config => { - _config = mergeConfigs(_config, config); - return getConfig(); - }; - - const interceptors = createInterceptors< - Request, - Response, - unknown, - RequestOptions - >(); - - // @ts-expect-error - const request: Client['request'] = async (options) => { - const opts = { - ..._config, - ...options, - fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, - headers: mergeHeaders(_config.headers, options.headers), - }; - - if (opts.security) { - await setAuthParams({ - ...opts, - security: opts.security, - }); - } - - if (opts.body && opts.bodySerializer) { - opts.body = opts.bodySerializer(opts.body); - } - - // remove Content-Type header if body is empty to avoid sending invalid requests - if (!opts.body) { - opts.headers.delete('Content-Type'); - } - - const url = buildUrl(opts); - const requestInit: ReqInit = { - redirect: 'follow', - ...opts, - }; - - let request = new Request(url, requestInit); - - for (const fn of interceptors.request._fns) { - request = await fn(request, opts); - } - - // fetch must be assigned here, otherwise it would throw the error: - // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation - const _fetch = opts.fetch!; - let response = await _fetch(request); - - for (const fn of interceptors.response._fns) { - response = await fn(response, request, opts); - } - - const result = { - request, - response, - }; - - if (response.ok) { - if ( - response.status === 204 || - response.headers.get('Content-Length') === '0' - ) { - return { - data: {}, - ...result, - }; - } - - const parseAs = - (opts.parseAs === 'auto' - ? getParseAs(response.headers.get('Content-Type')) - : opts.parseAs) ?? 'json'; - - if (parseAs === 'stream') { - return { - data: response.body, - ...result, - }; - } - - let data = await response[parseAs](); - if (parseAs === 'json') { - if (opts.responseValidator) { - await opts.responseValidator(data); - } - - if (opts.responseTransformer) { - data = await opts.responseTransformer(data); - } - } - - return { - data, - ...result, - }; - } - - let error = await response.text(); - - try { - error = JSON.parse(error); - } catch { - // noop - } - - let finalError = error; - - for (const fn of interceptors.error._fns) { - finalError = (await fn(error, response, request, opts)) as string; - } - - finalError = finalError || ({} as string); - - if (opts.throwOnError) { - throw finalError; - } - - return { - error: finalError, - ...result, - }; - }; - - return { - buildUrl, - connect: (options) => request({ ...options, method: 'CONNECT' }), - delete: (options) => request({ ...options, method: 'DELETE' }), - get: (options) => request({ ...options, method: 'GET' }), - getConfig, - head: (options) => request({ ...options, method: 'HEAD' }), - interceptors, - options: (options) => request({ ...options, method: 'OPTIONS' }), - patch: (options) => request({ ...options, method: 'PATCH' }), - post: (options) => request({ ...options, method: 'POST' }), - put: (options) => request({ ...options, method: 'PUT' }), - request, - setConfig, - trace: (options) => request({ ...options, method: 'TRACE' }), - }; -}; - -export type { - Auth, - Client, - Config, - Options, - OptionsLegacyParser, - RequestOptions, - RequestResult, -} from './types'; -export { - createConfig, - formDataBodySerializer, - jsonBodySerializer, - type QuerySerializerOptions, - urlSearchParamsBodySerializer, -} from './utils'; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/types.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/types.ts deleted file mode 100644 index d48f7e59d..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/types.ts +++ /dev/null @@ -1,241 +0,0 @@ -import type { - BodySerializer, - Middleware, - QuerySerializer, - QuerySerializerOptions, -} from './utils'; - -type OmitKeys = Pick>; - -export interface Config - extends Omit { - /** - * Auth token or a function returning auth token. The resolved value will be - * added to the request payload as defined by its `security` array. - */ - auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; - /** - * Base URL for all requests made by this client. - * - * @default '' - */ - baseUrl?: string; - /** - * A function for serializing request body parameter. By default, - * {@link JSON.stringify()} will be used. - */ - bodySerializer?: BodySerializer | null; - /** - * Fetch API implementation. You can use this option to provide a custom - * fetch instance. - * - * @default globalThis.fetch - */ - fetch?: (request: Request) => ReturnType; - /** - * An object containing any HTTP headers that you want to pre-populate your - * `Headers` object with. - * - * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} - */ - headers?: - | RequestInit['headers'] - | Record< - string, - | string - | number - | boolean - | (string | number | boolean)[] - | null - | undefined - | unknown - >; - /** - * The request method. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} - */ - method?: - | 'CONNECT' - | 'DELETE' - | 'GET' - | 'HEAD' - | 'OPTIONS' - | 'PATCH' - | 'POST' - | 'PUT' - | 'TRACE'; - /** - * Return the response data parsed in a specified format. By default, `auto` - * will infer the appropriate method from the `Content-Type` response header. - * You can override this behavior with any of the {@link Body} methods. - * Select `stream` if you don't want to parse response data at all. - * - * @default 'auto' - */ - parseAs?: Exclude | 'auto' | 'stream'; - /** - * A function for serializing request query parameters. By default, arrays - * will be exploded in form style, objects will be exploded in deepObject - * style, and reserved characters are percent-encoded. - * - * {@link https://swagger.io/docs/specification/serialization/#query View examples} - */ - querySerializer?: QuerySerializer | QuerySerializerOptions; - /** - * A function transforming response data before it's returned. This is useful - * for post-processing data, e.g. converting ISO strings into Date objects. - */ - responseTransformer?: (data: unknown) => Promise; - /** - * A function validating response data. This is useful if you want to ensure - * the response conforms to the desired shape, so it can be safely passed to - * the transformers and returned to the user. - */ - responseValidator?: (data: unknown) => Promise; - /** - * Throw an error instead of returning it in the response? - * - * @default false - */ - 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, -> extends Config { - /** - * Any body that you want to add to your request. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} - */ - body?: - | RequestInit['body'] - | Record - | Array> - | Array - | number; - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; - path?: Record; - query?: Record; - /** - * Security mechanism(s) to use for the request. - */ - security?: ReadonlyArray; - url: Url; -} - -export type RequestResult< - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = boolean, -> = ThrowOnError extends true - ? Promise<{ - data: TData; - request: Request; - response: Response; - }> - : Promise< - ( - | { data: TData; error: undefined } - | { data: undefined; error: TError } - ) & { - request: Request; - response: Response; - } - >; - -type MethodFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, ->( - options: Omit, 'method'>, -) => RequestResult; - -type RequestFn = < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, ->( - options: Omit, 'method'> & - Pick>, 'method'>, -) => RequestResult; - -export interface Client< - Req = Request, - Res = Response, - Err = unknown, - Opts = RequestOptions, -> { - /** - * Returns the final request URL. This method works only with experimental parser. - */ - buildUrl: < - TData extends { - body?: unknown; - path?: Record; - query?: Record; - url: string; - }, - >( - options: Pick & Options, - ) => string; - connect: MethodFn; - delete: MethodFn; - get: MethodFn; - getConfig: () => Config; - head: MethodFn; - interceptors: Middleware; - options: MethodFn; - patch: MethodFn; - post: MethodFn; - put: MethodFn; - request: RequestFn; - setConfig: (config: Config) => Config; - trace: MethodFn; -} - -interface DataShape { - body?: unknown; - headers?: unknown; - path?: unknown; - query?: unknown; - url: string; -} - -export type Options< - TData extends DataShape = DataShape, - ThrowOnError extends boolean = boolean, -> = 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; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/utils.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/utils.ts deleted file mode 100644 index b3039e5eb..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/bundle/client/utils.ts +++ /dev/null @@ -1,660 +0,0 @@ -import type { Auth, Client, Config, RequestOptions } from './types'; - -interface PathSerializer { - path: Record; - url: string; -} - -const PATH_PARAM_RE = /\{[^{}]+\}/g; - -type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; -type MatrixStyle = 'label' | 'matrix' | 'simple'; -type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type ObjectStyle = 'form' | 'deepObject'; -type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; - -export type QuerySerializer = (query: Record) => string; - -export type BodySerializer = (body: any) => any; - -interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; -} - -interface SerializeOptions - extends SerializePrimitiveOptions, - SerializerOptions {} -interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; -} -interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; -} - -export interface QuerySerializerOptions { - allowReserved?: boolean; - array?: SerializerOptions; - object?: SerializerOptions; -} - -const serializePrimitiveParam = ({ - allowReserved, - name, - value, -}: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ''; - } - - if (typeof value === 'object') { - throw new Error( - 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', - ); - } - - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; -}; - -const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; - default: - return ','; - } -}; - -const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: unknown[]; -}) => { - if (!explode) { - const joinedValues = ( - allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) - ).join(separatorArrayNoExplode(style)); - switch (style) { - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - case 'simple': - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } - - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === 'label' || style === 'simple') { - return allowReserved ? v : encodeURIComponent(v as string); - } - - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: Record | Date; -}) => { - if (value instanceof Date) { - return `${name}=${value.toISOString()}`; - } - - if (style !== 'deepObject' && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [ - ...values, - key, - allowReserved ? (v as string) : encodeURIComponent(v as string), - ]; - }); - const joinedValues = values.join(','); - switch (style) { - case 'form': - return `${name}=${joinedValues}`; - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } - - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { - let url = _url; - const matches = _url.match(PATH_PARAM_RE); - if (matches) { - for (const match of matches) { - let explode = false; - let name = match.substring(1, match.length - 1); - let style: ArraySeparatorStyle = 'simple'; - - if (name.endsWith('*')) { - explode = true; - name = name.substring(0, name.length - 1); - } - - if (name.startsWith('.')) { - name = name.substring(1); - style = 'label'; - } else if (name.startsWith(';')) { - name = name.substring(1); - style = 'matrix'; - } - - const value = path[name]; - - if (value === undefined || value === null) { - continue; - } - - if (Array.isArray(value)) { - url = url.replace( - match, - serializeArrayParam({ explode, name, style, value }), - ); - continue; - } - - if (typeof value === 'object') { - url = url.replace( - match, - serializeObjectParam({ - explode, - name, - style, - value: value as Record, - }), - ); - continue; - } - - if (style === 'matrix') { - url = url.replace( - match, - `;${serializePrimitiveParam({ - name, - value: value as string, - })}`, - ); - continue; - } - - const replaceValue = encodeURIComponent( - style === 'label' ? `.${value as string}` : (value as string), - ); - url = url.replace(match, replaceValue); - } - } - return url; -}; - -export const createQuerySerializer = ({ - allowReserved, - array, - object, -}: QuerySerializerOptions = {}) => { - const querySerializer = (queryParams: T) => { - let search: string[] = []; - if (queryParams && typeof queryParams === 'object') { - for (const name in queryParams) { - const value = queryParams[name]; - - if (value === undefined || value === null) { - continue; - } - - if (Array.isArray(value)) { - search = [ - ...search, - serializeArrayParam({ - allowReserved, - explode: true, - name, - style: 'form', - value, - ...array, - }), - ]; - continue; - } - - if (typeof value === 'object') { - search = [ - ...search, - serializeObjectParam({ - allowReserved, - explode: true, - name, - style: 'deepObject', - value: value as Record, - ...object, - }), - ]; - continue; - } - - search = [ - ...search, - serializePrimitiveParam({ - allowReserved, - name, - value: value as string, - }), - ]; - } - } - return search.join('&'); - }; - return querySerializer; -}; - -/** - * Infers parseAs value from provided Content-Type header. - */ -export const getParseAs = ( - contentType: string | null, -): Exclude => { - if (!contentType) { - // If no Content-Type header is provided, the best we can do is return the raw response body, - // which is effectively the same as the 'stream' option. - return 'stream'; - } - - const cleanContent = contentType.split(';')[0]?.trim(); - - if (!cleanContent) { - return; - } - - if ( - cleanContent.startsWith('application/json') || - cleanContent.endsWith('+json') - ) { - return 'json'; - } - - if (cleanContent === 'multipart/form-data') { - return 'formData'; - } - - if ( - ['application/', 'audio/', 'image/', 'video/'].some((type) => - cleanContent.startsWith(type), - ) - ) { - return 'blob'; - } - - if (cleanContent.startsWith('text/')) { - return 'text'; - } -}; - -export const getAuthToken = async ( - auth: Auth, - callback: RequestOptions['auth'], -): Promise => { - const token = - typeof callback === 'function' ? await callback(auth) : callback; - - if (!token) { - return; - } - - if (auth.scheme === 'bearer') { - return `Bearer ${token}`; - } - - if (auth.scheme === 'basic') { - return `Basic ${btoa(token)}`; - } - - return token; -}; - -export const setAuthParams = async ({ - security, - ...options -}: Pick, 'security'> & - Pick & { - headers: Headers; - }) => { - for (const auth of security) { - const token = await getAuthToken(auth, options.auth); - - if (!token) { - continue; - } - - const name = auth.name ?? 'Authorization'; - - 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; - } -}; - -export const buildUrl: Client['buildUrl'] = (options) => { - const url = getUrl({ - baseUrl: options.baseUrl ?? '', - path: options.path, - query: options.query, - querySerializer: - typeof options.querySerializer === 'function' - ? options.querySerializer - : createQuerySerializer(options.querySerializer), - url: options.url, - }); - return url; -}; - -export const getUrl = ({ - baseUrl, - path, - query, - querySerializer, - url: _url, -}: { - baseUrl: string; - path?: Record; - query?: Record; - querySerializer: QuerySerializer; - url: string; -}) => { - const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; - let url = baseUrl + pathUrl; - if (path) { - url = defaultPathSerializer({ path, url }); - } - let search = query ? querySerializer(query) : ''; - if (search.startsWith('?')) { - search = search.substring(1); - } - if (search) { - url += `?${search}`; - } - return url; -}; - -export const mergeConfigs = (a: Config, b: Config): Config => { - const config = { ...a, ...b }; - if (config.baseUrl?.endsWith('/')) { - config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); - } - config.headers = mergeHeaders(a.headers, b.headers); - return config; -}; - -export const mergeHeaders = ( - ...headers: Array['headers'] | undefined> -): Headers => { - const mergedHeaders = new Headers(); - for (const header of headers) { - if (!header || typeof header !== 'object') { - continue; - } - - const iterator = - header instanceof Headers ? header.entries() : Object.entries(header); - - for (const [key, value] of iterator) { - if (value === null) { - mergedHeaders.delete(key); - } else if (Array.isArray(value)) { - for (const v of value) { - mergedHeaders.append(key, v as string); - } - } else if (value !== undefined) { - // assume object headers are meant to be JSON stringified, i.e. their - // content value in OpenAPI specification is 'application/json' - mergedHeaders.set( - key, - typeof value === 'object' ? JSON.stringify(value) : (value as string), - ); - } - } - } - return mergedHeaders; -}; - -type ErrInterceptor = ( - error: Err, - response: Res, - request: Req, - options: Options, -) => Err | Promise; - -type ReqInterceptor = ( - request: Req, - options: Options, -) => Req | Promise; - -type ResInterceptor = ( - response: Res, - request: Req, - options: Options, -) => Res | Promise; - -class Interceptors { - _fns: Interceptor[]; - - constructor() { - this._fns = []; - } - - clear() { - this._fns = []; - } - - exists(fn: Interceptor) { - return this._fns.indexOf(fn) !== -1; - } - - eject(fn: Interceptor) { - const index = this._fns.indexOf(fn); - if (index !== -1) { - this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]; - } - } - - use(fn: Interceptor) { - this._fns = [...this._fns, fn]; - } -} - -// `createInterceptors()` response, meant for external use as it does not -// expose internals -export interface Middleware { - error: Pick< - Interceptors>, - 'eject' | 'use' - >; - request: Pick>, 'eject' | 'use'>; - response: Pick< - Interceptors>, - 'eject' | 'use' - >; -} - -// do not add `Middleware` as return type so we can use _fns internally -export const createInterceptors = () => ({ - error: new Interceptors>(), - request: new Interceptors>(), - response: new Interceptors>(), -}); - -const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { - if (typeof value === 'string' || value instanceof Blob) { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new FormData(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(data, key, v)); - } else { - serializeFormDataPair(data, key, value); - } - }); - - return data; - }, -}; - -export const jsonBodySerializer = { - bodySerializer: (body: T) => JSON.stringify(body), -}; - -const serializeUrlSearchParamsPair = ( - data: URLSearchParams, - key: string, - value: unknown, -) => { - if (typeof value === 'string') { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new URLSearchParams(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); - - return data; - }, -}; - -const defaultQuerySerializer = createQuerySerializer({ - allowReserved: false, - array: { - explode: true, - style: 'form', - }, - object: { - explode: true, - style: 'deepObject', - }, -}); - -const defaultHeaders = { - 'Content-Type': 'application/json', -}; - -export const createConfig = (override: Config = {}): Config => ({ - ...jsonBodySerializer, - baseUrl: '', - headers: defaultHeaders, - parseAs: 'auto', - querySerializer: defaultQuerySerializer, - ...override, -}); diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.cjs b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.cjs new file mode 100644 index 000000000..899cabc21 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.cjs @@ -0,0 +1,2 @@ +'use strict';var app=require('nuxt/app'),vue=require('vue');var $=async(r,t)=>{let e=typeof t=="function"?await t(r):t;if(e)return r.scheme==="bearer"?`Bearer ${e}`:r.scheme==="basic"?`Basic ${btoa(e)}`:e},P=(r,t,e)=>{typeof e=="string"||e instanceof Blob?r.append(t,e):r.append(t,JSON.stringify(e));},U=(r,t,e)=>{typeof e=="string"?r.append(t,e):r.append(t,JSON.stringify(e));},L={bodySerializer:r=>{let t=new FormData;return Object.entries(r).forEach(([e,s])=>{s!=null&&(Array.isArray(s)?s.forEach(o=>P(t,e,o)):P(t,e,s));}),t}},R={bodySerializer:r=>JSON.stringify(r)},H={bodySerializer:r=>{let t=new URLSearchParams;return Object.entries(r).forEach(([e,s])=>{s!=null&&(Array.isArray(s)?s.forEach(o=>U(t,e,o)):U(t,e,s));}),t}},D=r=>{switch(r){case "label":return ".";case "matrix":return ";";case "simple":return ",";default:return "&"}},N=r=>{switch(r){case "form":return ",";case "pipeDelimited":return "|";case "spaceDelimited":return "%20";default:return ","}},I=r=>{switch(r){case "label":return ".";case "matrix":return ";";case "simple":return ",";default:return "&"}},S=({allowReserved:r,explode:t,name:e,style:s,value:o})=>{if(!t){let n=(r?o:o.map(u=>encodeURIComponent(u))).join(N(s));switch(s){case "label":return `.${n}`;case "matrix":return `;${e}=${n}`;case "simple":return n;default:return `${e}=${n}`}}let a=D(s),i=o.map(n=>s==="label"||s==="simple"?r?n:encodeURIComponent(n):y({allowReserved:r,name:e,value:n})).join(a);return s==="label"||s==="matrix"?a+i:i},y=({allowReserved:r,name:t,value:e})=>{if(e==null)return "";if(typeof e=="object")throw new Error("Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these.");return `${t}=${r?e:encodeURIComponent(e)}`},A=({allowReserved:r,explode:t,name:e,style:s,value:o})=>{if(o instanceof Date)return `${e}=${o.toISOString()}`;if(s!=="deepObject"&&!t){let n=[];Object.entries(o).forEach(([l,p])=>{n=[...n,l,r?p:encodeURIComponent(p)];});let u=n.join(",");switch(s){case "form":return `${e}=${u}`;case "label":return `.${u}`;case "matrix":return `;${e}=${u}`;default:return u}}let a=I(s),i=Object.entries(o).map(([n,u])=>y({allowReserved:r,name:s==="deepObject"?`${e}[${n}]`:n,value:u})).join(a);return s==="label"||s==="matrix"?a+i:i};var Q=/\{[^{}]+\}/g,V=({path:r,url:t})=>{let e=t,s=t.match(Q);if(s)for(let o of s){let a=false,i=o.substring(1,o.length-1),n="simple";i.endsWith("*")&&(a=true,i=i.substring(0,i.length-1)),i.startsWith(".")?(i=i.substring(1),n="label"):i.startsWith(";")&&(i=i.substring(1),n="matrix");let u=vue.toValue(vue.toValue(r)[i]);if(u==null)continue;if(Array.isArray(u)){e=e.replace(o,S({explode:a,name:i,style:n,value:u}));continue}if(typeof u=="object"){e=e.replace(o,A({explode:a,name:i,style:n,value:u}));continue}if(n==="matrix"){e=e.replace(o,`;${y({name:i,value:u})}`);continue}let l=encodeURIComponent(n==="label"?`.${u}`:u);e=e.replace(o,l);}return e},E=({allowReserved:r,array:t,object:e}={})=>o=>{let a=[],i=vue.toValue(o);if(i&&typeof i=="object")for(let n in i){let u=vue.toValue(i[n]);if(u!=null){if(Array.isArray(u)){a=[...a,S({allowReserved:r,explode:true,name:n,style:"form",value:u,...t})];continue}if(typeof u=="object"){a=[...a,A({allowReserved:r,explode:true,name:n,style:"deepObject",value:u,...e})];continue}a=[...a,y({allowReserved:r,name:n,value:u})];}}return a.join("&")},k=async({security:r,...t})=>{for(let e of r){let s=await $(e,t.auth);if(!s)continue;let o=e.name??"Authorization";switch(e.in){case "query":t.query||(t.query={}),vue.toValue(t.query)[o]=s;break;case "header":default:t.headers.set(o,s);break}return}},d=r=>F({baseUrl:r.baseURL??"",path:r.path,query:r.query,querySerializer:typeof r.querySerializer=="function"?r.querySerializer:E(r.querySerializer),url:r.url}),F=({baseUrl:r,path:t,query:e,querySerializer:s,url:o})=>{let a=o.startsWith("/")?o:`/${o}`,i=r+a;t&&(i=V({path:t,url:i}));let n=e?s(e):"";return n.startsWith("?")&&(n=n.substring(1)),n&&(i+=`?${n}`),i},C=(r,t)=>{let e={...r,...t};return e.baseURL?.endsWith("/")&&(e.baseURL=e.baseURL.substring(0,e.baseURL.length-1)),e.headers=T(r.headers,t.headers),e},T=(...r)=>{let t=new Headers;for(let e of r){if(!e||typeof e!="object")continue;let s=e;vue.isRef(s)&&(s=vue.unref(s));let o=s instanceof Headers?s.entries():Object.entries(s);for(let[a,i]of o)if(i===null)t.delete(a);else if(Array.isArray(i))for(let n of i)t.append(a,f(n));else if(i!==undefined){let n=f(i);t.set(a,typeof n=="object"?JSON.stringify(n):n);}}return t},w=(...r)=>r.reduce((t,e)=>{if(typeof e=="function")t.push(e);else if(Array.isArray(e))return t.concat(e);return t},[]),W=E({allowReserved:false,array:{explode:true,style:"form"},object:{explode:true,style:"deepObject"}}),J={"Content-Type":"application/json"},q=(r={})=>({...R,baseURL:"",headers:J,querySerializer:W,...r}),f=r=>{if(r===null||typeof r!="object"||r instanceof Headers)return vue.isRef(r)?vue.unref(r):r;if(Array.isArray(r))return r.map(e=>f(e));if(vue.isRef(r))return f(vue.unref(r));let t={};for(let e in r)t[e]=f(r[e]);return t};var re=(r={})=>{let t=C(q(),r),e=()=>({...t}),s=a=>(t=C(t,a),e()),o=({asyncDataOptions:a,composable:i,key:n,...u})=>{let l={...t,...u,$fetch:u.$fetch??t.$fetch??$fetch,headers:T(t.headers,u.headers),onRequest:w(t.onRequest,u.onRequest),onResponse:w(t.onResponse,u.onResponse)},{responseTransformer:p,responseValidator:g,security:j}=l;j&&(l.onRequest=[async({options:c})=>{await k({auth:l.auth,headers:c.headers,query:c.query,security:j});},...l.onRequest]),(p||g)&&(l.onResponse=[...l.onResponse,async({options:c,response:b})=>{c.responseType&&c.responseType!=="json"||(g&&await g(b._data),p&&(b._data=await p(b._data)));}]),l.body&&l.bodySerializer&&(l.body=l.bodySerializer(f(l.body))),l.body||l.headers.delete("Content-Type");let O=l.$fetch;if(i==="$fetch"){let c=d(l);return O(c,f(l))}if(i==="useFetch"){let c=d(l);return app.useFetch(c,l)}if(i==="useLazyFetch"){let c=d(l);return app.useLazyFetch(c,l)}let h=()=>{let c=d(l);return O(c,f(l))};if(i==="useAsyncData")return n?app.useAsyncData(n,h,a):app.useAsyncData(h,a);if(i==="useLazyAsyncData")return n?app.useLazyAsyncData(n,h,a):app.useLazyAsyncData(h,a)};return {buildUrl:d,connect:a=>o({...a,method:"CONNECT"}),delete:a=>o({...a,method:"DELETE"}),get:a=>o({...a,method:"GET"}),getConfig:e,head:a=>o({...a,method:"HEAD"}),options:a=>o({...a,method:"OPTIONS"}),patch:a=>o({...a,method:"PATCH"}),post:a=>o({...a,method:"POST"}),put:a=>o({...a,method:"PUT"}),request:o,setConfig:s,trace:a=>o({...a,method:"TRACE"})}};exports.createClient=re;exports.createConfig=q;exports.formDataBodySerializer=L;exports.jsonBodySerializer=R;exports.urlSearchParamsBodySerializer=H;//# sourceMappingURL=index.cjs.map +//# sourceMappingURL=index.cjs.map \ No newline at end of file diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.d.cts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.d.cts new file mode 100644 index 000000000..9daa6d5b5 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.d.cts @@ -0,0 +1,160 @@ +import { AsyncDataOptions, useAsyncData, useFetch, useLazyAsyncData, useLazyFetch, UseFetchOptions } from 'nuxt/app'; +import { Ref } from 'vue'; + +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} +interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type ObjectStyle = 'form' | 'deepObject'; +type BodySerializer = (body: any) => any; +interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} +declare const formDataBodySerializer: { + bodySerializer: | Array>>(body: T) => FormData; +}; +declare const jsonBodySerializer: { + bodySerializer: (body: T) => string; +}; +declare const urlSearchParamsBodySerializer: { + bodySerializer: | Array>>(body: T) => URLSearchParams; +}; + +type QuerySerializer = (query: Parameters[0]['query']) => string; +type OmitKeys = Pick>; +type WithRefs = { + [K in keyof TData]: NonNullable extends object ? WithRefs> | Ref> : NonNullable | Ref>; +}; +interface Config extends Omit, 'baseURL' | 'body' | 'headers' | 'method' | 'query'>, WithRefs, 'query'>> { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * Base URL for all requests made by this client. + * + * @default '' + */ + baseURL?: string; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: RequestInit['headers'] | Record; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: 'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} +type AuthToken = string | undefined; +interface RequestOptions extends Config, WithRefs<{ + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: BodyInit | Record | null; + path?: FetchOptions['query']; + query?: FetchOptions['query']; +}> { + asyncDataOptions?: AsyncDataOptions; + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + composable: TComposable; + key?: string; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} +type RequestResult = TComposable extends '$fetch' ? ReturnType> : TComposable extends 'useAsyncData' ? ReturnType> : TComposable extends 'useFetch' ? ReturnType> : TComposable extends 'useLazyAsyncData' ? ReturnType> : TComposable extends 'useLazyFetch' ? ReturnType> : never; +type MethodFn = (options: Omit, 'method'>) => RequestResult; +type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; +interface DataShape { + body?: unknown; + headers?: unknown; + path?: FetchOptions['query']; + query?: FetchOptions['query']; + url: string; +} +type BuildUrlOptions = Omit> = Pick, 'path' | 'query'> & Pick & Pick, 'baseURL' | 'querySerializer'>; +interface Client { + /** + * Returns the final request URL. This method works only with experimental parser. + */ + buildUrl: >(options: BuildUrlOptions) => string; + connect: MethodFn; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; + trace: MethodFn; +} +type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & WithRefs>; +type OptionsLegacyParser = TData extends { + body?: any; +} ? TData extends { + headers?: any; +} ? OmitKeys & TData : OmitKeys & TData & Pick : TData extends { + headers?: any; +} ? OmitKeys & TData & Pick : OmitKeys & TData; +type FetchOptions = Omit, keyof AsyncDataOptions>; +type Composable = '$fetch' | 'useAsyncData' | 'useFetch' | 'useLazyAsyncData' | 'useLazyFetch'; + +declare const createConfig: (override?: Config) => Config; + +declare const createClient: (config?: Config) => Client; + +export { type Auth, type Client, type Composable, type Config, type Options, type OptionsLegacyParser, type QuerySerializerOptions, type RequestOptions, type RequestResult, createClient, createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.d.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.d.ts new file mode 100644 index 000000000..9daa6d5b5 --- /dev/null +++ b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.d.ts @@ -0,0 +1,160 @@ +import { AsyncDataOptions, useAsyncData, useFetch, useLazyAsyncData, useLazyFetch, UseFetchOptions } from 'nuxt/app'; +import { Ref } from 'vue'; + +interface Auth { + in?: 'header' | 'query'; + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} +interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} +type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +type ObjectStyle = 'form' | 'deepObject'; +type BodySerializer = (body: any) => any; +interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} +declare const formDataBodySerializer: { + bodySerializer: | Array>>(body: T) => FormData; +}; +declare const jsonBodySerializer: { + bodySerializer: (body: T) => string; +}; +declare const urlSearchParamsBodySerializer: { + bodySerializer: | Array>>(body: T) => URLSearchParams; +}; + +type QuerySerializer = (query: Parameters[0]['query']) => string; +type OmitKeys = Pick>; +type WithRefs = { + [K in keyof TData]: NonNullable extends object ? WithRefs> | Ref> : NonNullable | Ref>; +}; +interface Config extends Omit, 'baseURL' | 'body' | 'headers' | 'method' | 'query'>, WithRefs, 'query'>> { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * Base URL for all requests made by this client. + * + * @default '' + */ + baseURL?: string; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: RequestInit['headers'] | Record; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: 'CONNECT' | 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'PATCH' | 'POST' | 'PUT' | 'TRACE'; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} +type AuthToken = string | undefined; +interface RequestOptions extends Config, WithRefs<{ + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: BodyInit | Record | null; + path?: FetchOptions['query']; + query?: FetchOptions['query']; +}> { + asyncDataOptions?: AsyncDataOptions; + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + composable: TComposable; + key?: string; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} +type RequestResult = TComposable extends '$fetch' ? ReturnType> : TComposable extends 'useAsyncData' ? ReturnType> : TComposable extends 'useFetch' ? ReturnType> : TComposable extends 'useLazyAsyncData' ? ReturnType> : TComposable extends 'useLazyFetch' ? ReturnType> : never; +type MethodFn = (options: Omit, 'method'>) => RequestResult; +type RequestFn = (options: Omit, 'method'> & Pick>, 'method'>) => RequestResult; +interface DataShape { + body?: unknown; + headers?: unknown; + path?: FetchOptions['query']; + query?: FetchOptions['query']; + url: string; +} +type BuildUrlOptions = Omit> = Pick, 'path' | 'query'> & Pick & Pick, 'baseURL' | 'querySerializer'>; +interface Client { + /** + * Returns the final request URL. This method works only with experimental parser. + */ + buildUrl: >(options: BuildUrlOptions) => string; + connect: MethodFn; + delete: MethodFn; + get: MethodFn; + getConfig: () => Config; + head: MethodFn; + options: MethodFn; + patch: MethodFn; + post: MethodFn; + put: MethodFn; + request: RequestFn; + setConfig: (config: Config) => Config; + trace: MethodFn; +} +type Options = OmitKeys, 'body' | 'path' | 'query' | 'url'> & WithRefs>; +type OptionsLegacyParser = TData extends { + body?: any; +} ? TData extends { + headers?: any; +} ? OmitKeys & TData : OmitKeys & TData & Pick : TData extends { + headers?: any; +} ? OmitKeys & TData & Pick : OmitKeys & TData; +type FetchOptions = Omit, keyof AsyncDataOptions>; +type Composable = '$fetch' | 'useAsyncData' | 'useFetch' | 'useLazyAsyncData' | 'useLazyFetch'; + +declare const createConfig: (override?: Config) => Config; + +declare const createClient: (config?: Config) => Client; + +export { type Auth, type Client, type Composable, type Config, type Options, type OptionsLegacyParser, type QuerySerializerOptions, type RequestOptions, type RequestResult, createClient, createConfig, formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer }; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.ts deleted file mode 100644 index f76332582..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/index.ts +++ /dev/null @@ -1,169 +0,0 @@ -import type { NuxtApp } from 'nuxt/app'; -import { - useAsyncData, - useFetch, - useLazyAsyncData, - useLazyFetch, -} from 'nuxt/app'; - -import type { Client, Config } from './types'; -import { - buildUrl, - createConfig, - mergeConfigs, - mergeHeaders, - mergeInterceptors, - setAuthParams, - unwrapRefs, -} from './utils'; - -export const createClient = (config: Config = {}): Client => { - let _config = mergeConfigs(createConfig(), config); - - const getConfig = (): Config => ({ ..._config }); - - const setConfig = (config: Config): Config => { - _config = mergeConfigs(_config, config); - return getConfig(); - }; - - const request: Client['request'] = ({ - asyncDataOptions, - composable, - key, - ...options - }) => { - const opts = { - ..._config, - ...options, - $fetch: options.$fetch ?? _config.$fetch ?? $fetch, - headers: mergeHeaders(_config.headers, options.headers), - onRequest: mergeInterceptors(_config.onRequest, options.onRequest), - onResponse: mergeInterceptors(_config.onResponse, options.onResponse), - }; - - const { responseTransformer, responseValidator, security } = opts; - if (security) { - // auth must happen in interceptors otherwise we'd need to require - // asyncContext enabled - // https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext - opts.onRequest = [ - async ({ options }) => { - await setAuthParams({ - auth: opts.auth, - headers: options.headers, - query: options.query, - security, - }); - }, - ...opts.onRequest, - ]; - } - - if (responseTransformer || responseValidator) { - opts.onResponse = [ - ...opts.onResponse, - async ({ options, response }) => { - if (options.responseType && options.responseType !== 'json') { - return; - } - - if (responseValidator) { - await responseValidator(response._data); - } - - if (responseTransformer) { - response._data = await responseTransformer(response._data); - } - }, - ]; - } - - if (opts.body && opts.bodySerializer) { - opts.body = opts.bodySerializer(unwrapRefs(opts.body)); - } - - // remove Content-Type header if body is empty to avoid sending invalid requests - if (!opts.body) { - opts.headers.delete('Content-Type'); - } - - const fetchFn = opts.$fetch; - - if (composable === '$fetch') { - const url = buildUrl(opts); - return fetchFn( - url, - // @ts-expect-error - unwrapRefs(opts), - ); - } - - if (composable === 'useFetch') { - const url = buildUrl(opts); - return useFetch(url, opts); - } - - if (composable === 'useLazyFetch') { - const url = buildUrl(opts); - return useLazyFetch(url, opts); - } - - const handler: (ctx?: NuxtApp) => Promise = () => { - const url = buildUrl(opts); - return fetchFn( - url, - // @ts-expect-error - unwrapRefs(opts), - ); - }; - - if (composable === 'useAsyncData') { - return key - ? useAsyncData(key, handler, asyncDataOptions) - : useAsyncData(handler, asyncDataOptions); - } - - if (composable === 'useLazyAsyncData') { - return key - ? useLazyAsyncData(key, handler, asyncDataOptions) - : useLazyAsyncData(handler, asyncDataOptions); - } - - return undefined as any; - }; - - return { - buildUrl, - connect: (options) => request({ ...options, method: 'CONNECT' }), - delete: (options) => request({ ...options, method: 'DELETE' }), - get: (options) => request({ ...options, method: 'GET' }), - getConfig, - head: (options) => request({ ...options, method: 'HEAD' }), - options: (options) => request({ ...options, method: 'OPTIONS' }), - patch: (options) => request({ ...options, method: 'PATCH' }), - post: (options) => request({ ...options, method: 'POST' }), - put: (options) => request({ ...options, method: 'PUT' }), - request, - setConfig, - trace: (options) => request({ ...options, method: 'TRACE' }), - }; -}; - -export type { - Auth, - Client, - Composable, - Config, - Options, - OptionsLegacyParser, - QuerySerializerOptions, - RequestOptions, - RequestResult, -} from './types'; -export { - createConfig, - formDataBodySerializer, - jsonBodySerializer, - urlSearchParamsBodySerializer, -} from './utils'; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/types.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/types.ts deleted file mode 100644 index 0aff7480f..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/types.ts +++ /dev/null @@ -1,260 +0,0 @@ -import type { - AsyncDataOptions, - useAsyncData, - useFetch, - UseFetchOptions, - useLazyAsyncData, - useLazyFetch, -} from 'nuxt/app'; -import type { Ref } from 'vue'; - -export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; -type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; -type MatrixStyle = 'label' | 'matrix' | 'simple'; -export type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; -type ObjectStyle = 'form' | 'deepObject'; - -export type BodySerializer = (body: any) => any; - -export interface SerializerOptions { - /** - * @default true - */ - explode: boolean; - style: T; -} - -export type QuerySerializer = ( - query: Parameters[0]['query'], -) => string; - -export interface QuerySerializerOptions { - allowReserved?: boolean; - array?: SerializerOptions; - object?: SerializerOptions; -} - -type OmitKeys = Pick>; - -type WithRefs = { - [K in keyof TData]: NonNullable extends object - ? WithRefs> | Ref> - : NonNullable | Ref>; -}; - -export interface Config - extends Omit< - FetchOptions, - 'baseURL' | 'body' | 'headers' | 'method' | 'query' - >, - WithRefs, 'query'>> { - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * Auth token or a function returning auth token. The resolved value will be - * added to the request payload as defined by its `security` array. - */ - auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; - /** - * Base URL for all requests made by this client. - * - * @default '' - */ - baseURL?: string; - /** - * A function for serializing request body parameter. By default, - * {@link JSON.stringify()} will be used. - */ - bodySerializer?: BodySerializer | null; - /** - * An object containing any HTTP headers that you want to pre-populate your - * `Headers` object with. - * - * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} - */ - headers?: - | RequestInit['headers'] - | Record< - string, - | string - | number - | boolean - | (string | number | boolean)[] - | null - | undefined - | unknown - >; - /** - * The request method. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} - */ - method?: - | 'CONNECT' - | 'DELETE' - | 'GET' - | 'HEAD' - | 'OPTIONS' - | 'PATCH' - | 'POST' - | 'PUT' - | 'TRACE'; - /** - * A function for serializing request query parameters. By default, arrays - * will be exploded in form style, objects will be exploded in deepObject - * style, and reserved characters are percent-encoded. - * - * {@link https://swagger.io/docs/specification/serialization/#query View examples} - */ - querySerializer?: QuerySerializer | QuerySerializerOptions; - /** - * A function transforming response data before it's returned. This is useful - * for post-processing data, e.g. converting ISO strings into Date objects. - */ - responseTransformer?: (data: unknown) => Promise; - /** - * **This feature works only with the [experimental parser](https://heyapi.dev/openapi-ts/configuration#parser)** - * - * A function validating response data. This is useful if you want to ensure - * the response conforms to the desired shape, so it can be safely passed to - * the transformers and returned to the user. - */ - responseValidator?: (data: unknown) => Promise; -} - -export interface Auth { - in?: 'header' | 'query'; - name?: string; - scheme?: 'basic' | 'bearer'; - type: 'apiKey' | 'http'; -} - -type AuthToken = string | undefined; - -export interface RequestOptions< - TComposable extends Composable = Composable, - Url extends string = string, -> extends Config, - WithRefs<{ - /** - * Any body that you want to add to your request. - * - * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} - */ - body?: BodyInit | Record | null; - path?: FetchOptions['query']; - query?: FetchOptions['query']; - }> { - asyncDataOptions?: AsyncDataOptions; - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; - composable: TComposable; - key?: string; - /** - * Security mechanism(s) to use for the request. - */ - security?: ReadonlyArray; - url: Url; -} - -export type RequestResult< - TComposable extends Composable, - TData, - TError, -> = TComposable extends '$fetch' - ? ReturnType> - : TComposable extends 'useAsyncData' - ? ReturnType> - : TComposable extends 'useFetch' - ? ReturnType> - : TComposable extends 'useLazyAsyncData' - ? ReturnType> - : TComposable extends 'useLazyFetch' - ? ReturnType> - : never; - -type MethodFn = < - TComposable extends Composable, - TData = unknown, - TError = unknown, ->( - options: Omit, 'method'>, -) => RequestResult; - -type RequestFn = < - TComposable extends Composable, - TData = unknown, - TError = unknown, ->( - options: Omit, 'method'> & - Pick>, 'method'>, -) => RequestResult; - -interface DataShape { - body?: unknown; - headers?: unknown; - path?: FetchOptions['query']; - query?: FetchOptions['query']; - url: string; -} - -export type BuildUrlOptions< - TData extends Omit = Omit, -> = Pick, 'path' | 'query'> & - Pick & - Pick, 'baseURL' | 'querySerializer'>; - -export interface Client { - /** - * Returns the final request URL. This method works only with experimental parser. - */ - buildUrl: >( - options: BuildUrlOptions, - ) => string; - connect: MethodFn; - delete: MethodFn; - get: MethodFn; - getConfig: () => Config; - head: MethodFn; - options: MethodFn; - patch: MethodFn; - post: MethodFn; - put: MethodFn; - request: RequestFn; - setConfig: (config: Config) => Config; - trace: MethodFn; -} - -export type Options< - TComposable extends Composable, - TData extends DataShape = DataShape, -> = OmitKeys, 'body' | 'path' | 'query' | 'url'> & - WithRefs>; - -export type OptionsLegacyParser = TData extends { body?: any } - ? TData extends { headers?: any } - ? OmitKeys & TData - : OmitKeys & - TData & - Pick - : TData extends { headers?: any } - ? OmitKeys & - TData & - Pick - : OmitKeys & TData; - -type FetchOptions = Omit< - UseFetchOptions, - keyof AsyncDataOptions ->; - -export type Composable = - | '$fetch' - | 'useAsyncData' - | 'useFetch' - | 'useLazyAsyncData' - | 'useLazyFetch'; diff --git a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/utils.ts b/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/utils.ts deleted file mode 100644 index 97b064d92..000000000 --- a/packages/openapi-ts/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/bundle/client/utils.ts +++ /dev/null @@ -1,586 +0,0 @@ -import type { ComputedRef, Ref } from 'vue'; -import { isRef, toValue, unref } from 'vue'; - -import type { - ArraySeparatorStyle, - Auth, - BuildUrlOptions, - Client, - Config, - ObjectSeparatorStyle, - QuerySerializer, - QuerySerializerOptions, - RequestOptions, - SerializerOptions, -} from './types'; - -type PathSerializer = Pick, 'path' | 'url'>; - -const PATH_PARAM_RE = /\{[^{}]+\}/g; - -type MaybeArray = T | T[]; - -interface SerializeOptions - extends SerializePrimitiveOptions, - SerializerOptions {} -interface SerializePrimitiveOptions { - allowReserved?: boolean; - name: string; -} -interface SerializePrimitiveParam extends SerializePrimitiveOptions { - value: string; -} - -const serializePrimitiveParam = ({ - allowReserved, - name, - value, -}: SerializePrimitiveParam) => { - if (value === undefined || value === null) { - return ''; - } - - if (typeof value === 'object') { - throw new Error( - 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', - ); - } - - return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; -}; - -const separatorArrayExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { - switch (style) { - case 'form': - return ','; - case 'pipeDelimited': - return '|'; - case 'spaceDelimited': - return '%20'; - default: - return ','; - } -}; - -const separatorObjectExplode = (style: ObjectSeparatorStyle) => { - switch (style) { - case 'label': - return '.'; - case 'matrix': - return ';'; - case 'simple': - return ','; - default: - return '&'; - } -}; - -const serializeArrayParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: unknown[]; -}) => { - if (!explode) { - const joinedValues = ( - allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) - ).join(separatorArrayNoExplode(style)); - switch (style) { - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - case 'simple': - return joinedValues; - default: - return `${name}=${joinedValues}`; - } - } - - const separator = separatorArrayExplode(style); - const joinedValues = value - .map((v) => { - if (style === 'label' || style === 'simple') { - return allowReserved ? v : encodeURIComponent(v as string); - } - - return serializePrimitiveParam({ - allowReserved, - name, - value: v as string, - }); - }) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const serializeObjectParam = ({ - allowReserved, - explode, - name, - style, - value, -}: SerializeOptions & { - value: Record | Date; -}) => { - if (value instanceof Date) { - return `${name}=${value.toISOString()}`; - } - - if (style !== 'deepObject' && !explode) { - let values: string[] = []; - Object.entries(value).forEach(([key, v]) => { - values = [ - ...values, - key, - allowReserved ? (v as string) : encodeURIComponent(v as string), - ]; - }); - const joinedValues = values.join(','); - switch (style) { - case 'form': - return `${name}=${joinedValues}`; - case 'label': - return `.${joinedValues}`; - case 'matrix': - return `;${name}=${joinedValues}`; - default: - return joinedValues; - } - } - - const separator = separatorObjectExplode(style); - const joinedValues = Object.entries(value) - .map(([key, v]) => - serializePrimitiveParam({ - allowReserved, - name: style === 'deepObject' ? `${name}[${key}]` : key, - value: v as string, - }), - ) - .join(separator); - return style === 'label' || style === 'matrix' - ? separator + joinedValues - : joinedValues; -}; - -const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { - let url = _url; - const matches = _url.match(PATH_PARAM_RE); - if (matches) { - for (const match of matches) { - let explode = false; - let name = match.substring(1, match.length - 1); - let style: ArraySeparatorStyle = 'simple'; - - if (name.endsWith('*')) { - explode = true; - name = name.substring(0, name.length - 1); - } - - if (name.startsWith('.')) { - name = name.substring(1); - style = 'label'; - } else if (name.startsWith(';')) { - name = name.substring(1); - style = 'matrix'; - } - - const value = toValue(toValue(path)[name]); - - if (value === undefined || value === null) { - continue; - } - - if (Array.isArray(value)) { - url = url.replace( - match, - serializeArrayParam({ explode, name, style, value }), - ); - continue; - } - - if (typeof value === 'object') { - url = url.replace( - match, - serializeObjectParam({ - explode, - name, - style, - value: value as Record, - }), - ); - continue; - } - - if (style === 'matrix') { - url = url.replace( - match, - `;${serializePrimitiveParam({ - name, - value: value as string, - })}`, - ); - continue; - } - - const replaceValue = encodeURIComponent( - style === 'label' ? `.${value as string}` : (value as string), - ); - url = url.replace(match, replaceValue); - } - } - return url; -}; - -export const createQuerySerializer = ({ - allowReserved, - array, - object, -}: QuerySerializerOptions = {}) => { - const querySerializer = (queryParams: T) => { - let search: string[] = []; - const qParams = toValue(queryParams); - if (qParams && typeof qParams === 'object') { - for (const name in qParams) { - const value = toValue(qParams[name]); - - if (value === undefined || value === null) { - continue; - } - - if (Array.isArray(value)) { - search = [ - ...search, - serializeArrayParam({ - allowReserved, - explode: true, - name, - style: 'form', - value, - ...array, - }), - ]; - continue; - } - - if (typeof value === 'object') { - search = [ - ...search, - serializeObjectParam({ - allowReserved, - explode: true, - name, - style: 'deepObject', - value: value as Record, - ...object, - }), - ]; - continue; - } - - search = [ - ...search, - serializePrimitiveParam({ - allowReserved, - name, - value: value as string, - }), - ]; - } - } - return search.join('&'); - }; - return querySerializer; -}; - -export const getAuthToken = async ( - auth: Auth, - callback: RequestOptions['auth'], -): Promise => { - const token = - typeof callback === 'function' ? await callback(auth) : callback; - - if (!token) { - return; - } - - if (auth.scheme === 'bearer') { - return `Bearer ${token}`; - } - - if (auth.scheme === 'basic') { - return `Basic ${btoa(token)}`; - } - - return token; -}; - -export const setAuthParams = async ({ - security, - ...options -}: Pick, 'security'> & - Pick & { - headers: Headers; - }) => { - for (const auth of security) { - const token = await getAuthToken(auth, options.auth); - - if (!token) { - continue; - } - - const name = auth.name ?? 'Authorization'; - - switch (auth.in) { - case 'query': - if (!options.query) { - options.query = {}; - } - toValue(options.query)[name] = token; - break; - case 'header': - default: - options.headers.set(name, token); - break; - } - - return; - } -}; - -export const buildUrl: Client['buildUrl'] = (options) => { - const url = getUrl({ - baseUrl: options.baseURL ?? '', - path: options.path, - query: options.query, - querySerializer: - typeof options.querySerializer === 'function' - ? options.querySerializer - : createQuerySerializer(options.querySerializer), - url: options.url, - }); - return url; -}; - -export const getUrl = ({ - baseUrl, - path, - query, - querySerializer, - url: _url, -}: Pick & { - baseUrl: string; - querySerializer: QuerySerializer; -}) => { - const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; - let url = baseUrl + pathUrl; - if (path) { - url = defaultPathSerializer({ path, url }); - } - let search = query ? querySerializer(query) : ''; - if (search.startsWith('?')) { - search = search.substring(1); - } - if (search) { - url += `?${search}`; - } - return url; -}; - -export const mergeConfigs = (a: Config, b: Config): Config => { - const config = { ...a, ...b }; - if (config.baseURL?.endsWith('/')) { - config.baseURL = config.baseURL.substring(0, config.baseURL.length - 1); - } - config.headers = mergeHeaders(a.headers, b.headers); - return config; -}; - -export const mergeHeaders = ( - ...headers: Array['headers'] | undefined> -): Headers => { - const mergedHeaders = new Headers(); - for (const header of headers) { - if (!header || typeof header !== 'object') { - continue; - } - - let h: unknown = header; - if (isRef(h)) { - h = unref(h); - } - - const iterator = - h instanceof Headers - ? h.entries() - : Object.entries(h as Record); - - for (const [key, value] of iterator) { - if (value === null) { - mergedHeaders.delete(key); - } else if (Array.isArray(value)) { - for (const v of value) { - mergedHeaders.append(key, unwrapRefs(v) as string); - } - } else if (value !== undefined) { - const v = unwrapRefs(value); - // assume object headers are meant to be JSON stringified, i.e. their - // content value in OpenAPI specification is 'application/json' - mergedHeaders.set( - key, - typeof v === 'object' ? JSON.stringify(v) : (v as string), - ); - } - } - } - return mergedHeaders; -}; - -export const mergeInterceptors = (...args: Array>): Array => - args.reduce>((acc, item) => { - if (typeof item === 'function') { - acc.push(item); - } else if (Array.isArray(item)) { - return acc.concat(item); - } - return acc; - }, []); - -const serializeFormDataPair = (data: FormData, key: string, value: unknown) => { - if (typeof value === 'string' || value instanceof Blob) { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const formDataBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new FormData(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeFormDataPair(data, key, v)); - } else { - serializeFormDataPair(data, key, value); - } - }); - - return data; - }, -}; - -export const jsonBodySerializer = { - bodySerializer: (body: T) => JSON.stringify(body), -}; - -const serializeUrlSearchParamsPair = ( - data: URLSearchParams, - key: string, - value: unknown, -) => { - if (typeof value === 'string') { - data.append(key, value); - } else { - data.append(key, JSON.stringify(value)); - } -}; - -export const urlSearchParamsBodySerializer = { - bodySerializer: | Array>>( - body: T, - ) => { - const data = new URLSearchParams(); - - Object.entries(body).forEach(([key, value]) => { - if (value === undefined || value === null) { - return; - } - if (Array.isArray(value)) { - value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); - } else { - serializeUrlSearchParamsPair(data, key, value); - } - }); - - return data; - }, -}; - -const defaultQuerySerializer = createQuerySerializer({ - allowReserved: false, - array: { - explode: true, - style: 'form', - }, - object: { - explode: true, - style: 'deepObject', - }, -}); - -const defaultHeaders = { - 'Content-Type': 'application/json', -}; - -export const createConfig = (override: Config = {}): Config => ({ - ...jsonBodySerializer, - baseURL: '', - headers: defaultHeaders, - querySerializer: defaultQuerySerializer, - ...override, -}); - -type UnwrapRefs = - T extends Ref - ? V - : T extends ComputedRef - ? V - : T extends Record // this doesn't handle functions well - ? { [K in keyof T]: UnwrapRefs } - : T; - -export const unwrapRefs = (value: T): UnwrapRefs => { - if (value === null || typeof value !== 'object' || value instanceof Headers) { - return (isRef(value) ? unref(value) : value) as UnwrapRefs; - } - - if (Array.isArray(value)) { - return value.map((item) => unwrapRefs(item)) as UnwrapRefs; - } - - if (isRef(value)) { - return unwrapRefs(unref(value) as T); - } - - // unwrap into new object to avoid modifying the source - const result: Record = {}; - for (const key in value) { - result[key] = unwrapRefs(value[key] as T); - } - return result as UnwrapRefs; -}; diff --git a/packages/openapi-ts/test/openapi-ts.config.ts b/packages/openapi-ts/test/openapi-ts.config.ts index f6e68ffd5..cb450d46f 100644 --- a/packages/openapi-ts/test/openapi-ts.config.ts +++ b/packages/openapi-ts/test/openapi-ts.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from '../src'; export default defineConfig({ client: { - // bundle: true, + bundle: true, name: '@hey-api/client-fetch', // name: 'legacy/xhr', }, @@ -11,7 +11,7 @@ export default defineConfig({ // exclude: '^#/components/schemas/ModelWithCircularReference$', // include: // '^(#/components/schemas/import|#/paths/api/v{api-version}/simple/options)$', - path: './packages/openapi-ts/test/spec/2.0.x/body-response-text-plain.yaml', + path: './packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml', // path: './test/spec/v3-transforms.json', // path: 'https://mongodb-mms-prod-build-server.s3.amazonaws.com/openapi/2caffd88277a4e27c95dcefc7e3b6a63a3b03297-v2-2023-11-15.json', // path: 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', diff --git a/packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml b/packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml new file mode 100644 index 000000000..4af61c5fc --- /dev/null +++ b/packages/openapi-ts/test/spec/3.1.x/body-nested-array.yaml @@ -0,0 +1,25 @@ +openapi: 3.1.1 +info: + title: OpenAPI 3.1.1 body nested array example + version: 1 +paths: + /foo: + post: + requestBody: + content: + 'multipart/form-data': + schema: + properties: + foo: + items: + items: + type: integer + type: array + type: array + required: + - foo + type: object + required: true + responses: + '200': + description: OK diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc8aa5592..29099a09d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,12 @@ importers: prettier: specifier: 3.4.2 version: 3.4.2 + rollup: + specifier: 4.31.0 + version: 4.31.0 + rollup-plugin-dts: + specifier: 6.1.1 + version: 6.1.1(rollup@4.31.0)(typescript@5.5.3) tsup: specifier: 8.3.5 version: 8.3.5(jiti@2.4.2)(postcss@8.4.49)(typescript@5.5.3)(yaml@2.7.0) @@ -260,7 +266,7 @@ importers: version: link:../../packages/client-nuxt nuxt: specifier: 3.15.1 - version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(yaml@2.7.0) + version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(yaml@2.7.0) vue: specifier: 3.5.13 version: 3.5.13(typescript@5.6.1-rc) @@ -392,7 +398,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: ^19.0.6 - version: 19.0.6(@angular/compiler-cli@19.0.5(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3))(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.5)(chokidar@4.0.3)(karma@6.4.4)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.5.3)))(typescript@5.5.3)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + version: 19.0.6(@angular/compiler-cli@19.0.5(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3))(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.5)(chokidar@4.0.3)(karma@6.4.4)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.5.3)))(typescript@5.5.3)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) '@angular/cli': specifier: ^19.0.6 version: 19.0.6(@types/node@22.10.5)(chokidar@4.0.3) @@ -653,7 +659,7 @@ importers: version: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) vite-plugin-vue-devtools: specifier: 7.7.0 - version: 7.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) + version: 7.7.0(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) vitest: specifier: 1.6.0 version: 1.6.0(@types/node@22.10.5)(jsdom@23.0.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) @@ -663,26 +669,36 @@ importers: packages/client-axios: devDependencies: + '@hey-api/client-core': + specifier: workspace:* + version: link:../client-core axios: specifier: 1.7.9 version: 1.7.9 packages/client-core: {} - packages/client-fetch: {} + packages/client-fetch: + devDependencies: + '@hey-api/client-core': + specifier: workspace:* + version: link:../client-core packages/client-nuxt: dependencies: nuxt: specifier: '>= 3.0.0 < 4' - version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0) + version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0) vue: specifier: '>= 3.5.13 < 4' version: 3.5.13(typescript@5.6.1-rc) devDependencies: + '@hey-api/client-core': + specifier: workspace:* + version: link:../client-core '@nuxt/test-utils': specifier: 3.15.1 - version: 3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + version: 3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) packages/openapi-ts: dependencies: @@ -701,7 +717,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: 19.0.6 - version: 19.0.6(@angular/compiler-cli@19.0.5(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3))(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.5)(chokidar@4.0.3)(karma@6.4.4)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.5.3)))(typescript@5.5.3)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + version: 19.0.6(@angular/compiler-cli@19.0.5(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.5.3))(@angular/compiler@19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.5)(chokidar@4.0.3)(karma@6.4.4)(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.5.3)))(typescript@5.5.3)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) '@angular/animations': specifier: 19.0.5 version: 19.0.5(@angular/core@19.0.5(rxjs@7.8.1)(zone.js@0.15.0)) @@ -785,7 +801,7 @@ importers: version: 3.3.2 nuxt: specifier: 3.15.1 - version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0) + version: 3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))(yaml@2.7.0) prettier: specifier: 3.4.2 version: 3.4.2 @@ -3684,8 +3700,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.30.1': - resolution: {integrity: sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==} + '@rollup/rollup-android-arm-eabi@4.31.0': + resolution: {integrity: sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==} cpu: [arm] os: [android] @@ -3694,8 +3710,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.30.1': - resolution: {integrity: sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==} + '@rollup/rollup-android-arm64@4.31.0': + resolution: {integrity: sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==} cpu: [arm64] os: [android] @@ -3704,8 +3720,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.30.1': - resolution: {integrity: sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==} + '@rollup/rollup-darwin-arm64@4.31.0': + resolution: {integrity: sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==} cpu: [arm64] os: [darwin] @@ -3714,8 +3730,8 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.30.1': - resolution: {integrity: sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==} + '@rollup/rollup-darwin-x64@4.31.0': + resolution: {integrity: sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==} cpu: [x64] os: [darwin] @@ -3724,8 +3740,8 @@ packages: cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.30.1': - resolution: {integrity: sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==} + '@rollup/rollup-freebsd-arm64@4.31.0': + resolution: {integrity: sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==} cpu: [arm64] os: [freebsd] @@ -3734,8 +3750,8 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.30.1': - resolution: {integrity: sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==} + '@rollup/rollup-freebsd-x64@4.31.0': + resolution: {integrity: sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==} cpu: [x64] os: [freebsd] @@ -3744,8 +3760,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': - resolution: {integrity: sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==} + '@rollup/rollup-linux-arm-gnueabihf@4.31.0': + resolution: {integrity: sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==} cpu: [arm] os: [linux] @@ -3754,8 +3770,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.30.1': - resolution: {integrity: sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==} + '@rollup/rollup-linux-arm-musleabihf@4.31.0': + resolution: {integrity: sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==} cpu: [arm] os: [linux] @@ -3764,8 +3780,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.30.1': - resolution: {integrity: sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==} + '@rollup/rollup-linux-arm64-gnu@4.31.0': + resolution: {integrity: sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==} cpu: [arm64] os: [linux] @@ -3774,13 +3790,13 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.30.1': - resolution: {integrity: sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==} + '@rollup/rollup-linux-arm64-musl@4.31.0': + resolution: {integrity: sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': - resolution: {integrity: sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.31.0': + resolution: {integrity: sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==} cpu: [loong64] os: [linux] @@ -3789,8 +3805,8 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': - resolution: {integrity: sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': + resolution: {integrity: sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==} cpu: [ppc64] os: [linux] @@ -3799,8 +3815,8 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.30.1': - resolution: {integrity: sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==} + '@rollup/rollup-linux-riscv64-gnu@4.31.0': + resolution: {integrity: sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==} cpu: [riscv64] os: [linux] @@ -3809,8 +3825,8 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.30.1': - resolution: {integrity: sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==} + '@rollup/rollup-linux-s390x-gnu@4.31.0': + resolution: {integrity: sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==} cpu: [s390x] os: [linux] @@ -3819,8 +3835,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.30.1': - resolution: {integrity: sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==} + '@rollup/rollup-linux-x64-gnu@4.31.0': + resolution: {integrity: sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==} cpu: [x64] os: [linux] @@ -3829,8 +3845,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.30.1': - resolution: {integrity: sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==} + '@rollup/rollup-linux-x64-musl@4.31.0': + resolution: {integrity: sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==} cpu: [x64] os: [linux] @@ -3839,8 +3855,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.30.1': - resolution: {integrity: sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==} + '@rollup/rollup-win32-arm64-msvc@4.31.0': + resolution: {integrity: sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==} cpu: [arm64] os: [win32] @@ -3849,8 +3865,8 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.30.1': - resolution: {integrity: sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==} + '@rollup/rollup-win32-ia32-msvc@4.31.0': + resolution: {integrity: sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==} cpu: [ia32] os: [win32] @@ -3859,8 +3875,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.30.1': - resolution: {integrity: sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==} + '@rollup/rollup-win32-x64-msvc@4.31.0': + resolution: {integrity: sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==} cpu: [x64] os: [win32] @@ -8698,6 +8714,13 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true + rollup-plugin-dts@6.1.1: + resolution: {integrity: sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + rollup-plugin-visualizer@5.14.0: resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==} engines: {node: '>=18'} @@ -8716,8 +8739,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.30.1: - resolution: {integrity: sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==} + rollup@4.31.0: + resolution: {integrity: sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -12677,9 +12700,20 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))': + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': + dependencies: + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) + '@nuxt/schema': 3.15.1 + execa: 7.2.0 + vite: 5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + transitivePeerDependencies: + - magicast + - rollup + - supports-color + + '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))': dependencies: - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) '@nuxt/schema': 3.15.1 execa: 7.2.0 vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) @@ -12701,13 +12735,13 @@ snapshots: rc9: 2.1.2 semver: 7.6.3 - '@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3))': + '@nuxt/devtools@1.7.0(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))(vue@3.5.13(typescript@5.5.3))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) '@nuxt/devtools-wizard': 1.7.0 - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) - '@vue/devtools-core': 7.6.8(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) + '@vue/devtools-core': 7.6.8(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))(vue@3.5.13(typescript@5.5.3)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.3.3 @@ -12735,10 +12769,10 @@ snapshots: simple-git: 3.27.0 sirv: 3.0.0 tinyglobby: 0.2.10 - unimport: 3.14.5(rollup@4.30.1) - vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1))(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) - vite-plugin-vue-inspector: 5.3.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + unimport: 3.14.5(rollup@4.31.0) + vite: 5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0))(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + vite-plugin-vue-inspector: 5.3.1(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) which: 3.0.1 ws: 8.18.0 transitivePeerDependencies: @@ -12748,12 +12782,12 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc))': + '@nuxt/devtools@1.7.0(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) '@nuxt/devtools-wizard': 1.7.0 - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) '@vue/devtools-core': 7.6.8(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 @@ -12782,9 +12816,9 @@ snapshots: simple-git: 3.27.0 sirv: 3.0.0 tinyglobby: 0.2.10 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1))(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0))(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) vite-plugin-vue-inspector: 5.3.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) which: 3.0.1 ws: 8.18.0 @@ -12795,13 +12829,13 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.30.1)(vue@3.5.13(typescript@5.6.1-rc))': + '@nuxt/devtools@1.7.0(rollup@4.31.0)(vue@3.5.13(typescript@5.6.1-rc))': dependencies: '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) '@nuxt/devtools-wizard': 1.7.0 - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) - '@vue/devtools-core': 7.6.8(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc)) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) + '@vue/devtools-core': 7.6.8(vue@3.5.13(typescript@5.6.1-rc)) '@vue/devtools-kit': 7.6.8 birpc: 0.2.19 consola: 3.3.3 @@ -12829,9 +12863,9 @@ snapshots: simple-git: 3.27.0 sirv: 3.0.0 tinyglobby: 0.2.10 - unimport: 3.14.5(rollup@4.30.1) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1))(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) - vite-plugin-vue-inspector: 5.3.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + unimport: 3.14.5(rollup@4.31.0) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0))(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + vite-plugin-vue-inspector: 5.3.1(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) which: 3.0.1 ws: 8.18.0 transitivePeerDependencies: @@ -12841,7 +12875,7 @@ snapshots: - utf-8-validate - vue - '@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1)': + '@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0)': dependencies: '@nuxt/schema': 3.15.1 c12: 2.0.1(magicast@0.3.5) @@ -12861,7 +12895,7 @@ snapshots: semver: 7.6.3 ufo: 1.5.4 unctx: 2.4.1 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) untyped: 1.5.2 transitivePeerDependencies: - magicast @@ -12875,9 +12909,9 @@ snapshots: pathe: 2.0.1 std-env: 3.8.0 - '@nuxt/telemetry@2.6.4(magicast@0.3.5)(rollup@4.30.1)': + '@nuxt/telemetry@2.6.4(magicast@0.3.5)(rollup@4.31.0)': dependencies: - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) citty: 0.1.6 consola: 3.3.3 destr: 2.0.3 @@ -12895,9 +12929,9 @@ snapshots: - rollup - supports-color - '@nuxt/test-utils@3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': + '@nuxt/test-utils@3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': dependencies: - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) '@nuxt/schema': 3.15.1 c12: 2.0.1(magicast@0.3.5) consola: 3.3.3 @@ -12921,7 +12955,7 @@ snapshots: unenv: 1.10.0 unplugin: 2.1.2 vite: 5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) - vitest-environment-nuxt: 1.0.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + vitest-environment-nuxt: 1.0.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) vue: 3.5.13(typescript@5.6.1-rc) optionalDependencies: '@vue/test-utils': 2.4.6 @@ -12941,10 +12975,10 @@ snapshots: - terser - typescript - '@nuxt/vite-builder@3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vue@3.5.13(typescript@5.5.3))(yaml@2.7.0)': + '@nuxt/vite-builder@3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vue@3.5.13(typescript@5.5.3))(yaml@2.7.0)': dependencies: - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) - '@rollup/plugin-replace': 6.0.2(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) + '@rollup/plugin-replace': 6.0.2(rollup@4.31.0) '@vitejs/plugin-vue': 5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) '@vitejs/plugin-vue-jsx': 4.1.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) autoprefixer: 10.4.20(postcss@8.4.49) @@ -12965,7 +12999,7 @@ snapshots: perfect-debounce: 1.0.0 pkg-types: 1.3.0 postcss: 8.4.49 - rollup-plugin-visualizer: 5.14.0(rollup@4.30.1) + rollup-plugin-visualizer: 5.14.0(rollup@4.31.0) std-env: 3.8.0 ufo: 1.5.4 unenv: 1.10.0 @@ -13000,10 +13034,10 @@ snapshots: - vue-tsc - yaml - '@nuxt/vite-builder@3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vue@3.5.13(typescript@5.6.1-rc))(yaml@2.7.0)': + '@nuxt/vite-builder@3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vue@3.5.13(typescript@5.6.1-rc))(yaml@2.7.0)': dependencies: - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) - '@rollup/plugin-replace': 6.0.2(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) + '@rollup/plugin-replace': 6.0.2(rollup@4.31.0) '@vitejs/plugin-vue': 5.2.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc)) '@vitejs/plugin-vue-jsx': 4.1.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc)) autoprefixer: 10.4.20(postcss@8.4.49) @@ -13024,7 +13058,7 @@ snapshots: perfect-debounce: 1.0.0 pkg-types: 1.3.0 postcss: 8.4.49 - rollup-plugin-visualizer: 5.14.0(rollup@4.30.1) + rollup-plugin-visualizer: 5.14.0(rollup@4.31.0) std-env: 3.8.0 ufo: 1.5.4 unenv: 1.10.0 @@ -13819,13 +13853,13 @@ snapshots: - encoding - supports-color - '@rollup/plugin-alias@5.1.1(rollup@4.30.1)': + '@rollup/plugin-alias@5.1.1(rollup@4.31.0)': optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/plugin-commonjs@28.0.2(rollup@4.30.1)': + '@rollup/plugin-commonjs@28.0.2(rollup@4.31.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.2(picomatch@4.0.2) @@ -13833,164 +13867,164 @@ snapshots: magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/plugin-inject@5.0.5(rollup@4.30.1)': + '@rollup/plugin-inject@5.0.5(rollup@4.31.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) estree-walker: 2.0.2 magic-string: 0.30.17 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/plugin-json@6.1.0(rollup@4.30.1)': + '@rollup/plugin-json@6.1.0(rollup@4.31.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/plugin-node-resolve@15.3.1(rollup@4.30.1)': + '@rollup/plugin-node-resolve@15.3.1(rollup@4.31.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/plugin-replace@6.0.2(rollup@4.30.1)': + '@rollup/plugin-replace@6.0.2(rollup@4.31.0)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) magic-string: 0.30.17 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/plugin-terser@0.4.4(rollup@4.30.1)': + '@rollup/plugin-terser@0.4.4(rollup@4.31.0)': dependencies: serialize-javascript: 6.0.2 smob: 1.5.0 terser: 5.36.0 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 - '@rollup/pluginutils@5.1.4(rollup@4.30.1)': + '@rollup/pluginutils@5.1.4(rollup@4.31.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 '@rollup/rollup-android-arm-eabi@4.26.0': optional: true - '@rollup/rollup-android-arm-eabi@4.30.1': + '@rollup/rollup-android-arm-eabi@4.31.0': optional: true '@rollup/rollup-android-arm64@4.26.0': optional: true - '@rollup/rollup-android-arm64@4.30.1': + '@rollup/rollup-android-arm64@4.31.0': optional: true '@rollup/rollup-darwin-arm64@4.26.0': optional: true - '@rollup/rollup-darwin-arm64@4.30.1': + '@rollup/rollup-darwin-arm64@4.31.0': optional: true '@rollup/rollup-darwin-x64@4.26.0': optional: true - '@rollup/rollup-darwin-x64@4.30.1': + '@rollup/rollup-darwin-x64@4.31.0': optional: true '@rollup/rollup-freebsd-arm64@4.26.0': optional: true - '@rollup/rollup-freebsd-arm64@4.30.1': + '@rollup/rollup-freebsd-arm64@4.31.0': optional: true '@rollup/rollup-freebsd-x64@4.26.0': optional: true - '@rollup/rollup-freebsd-x64@4.30.1': + '@rollup/rollup-freebsd-x64@4.31.0': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.26.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.30.1': + '@rollup/rollup-linux-arm-gnueabihf@4.31.0': optional: true '@rollup/rollup-linux-arm-musleabihf@4.26.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.30.1': + '@rollup/rollup-linux-arm-musleabihf@4.31.0': optional: true '@rollup/rollup-linux-arm64-gnu@4.26.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.30.1': + '@rollup/rollup-linux-arm64-gnu@4.31.0': optional: true '@rollup/rollup-linux-arm64-musl@4.26.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.30.1': + '@rollup/rollup-linux-arm64-musl@4.31.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.30.1': + '@rollup/rollup-linux-loongarch64-gnu@4.31.0': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.26.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.30.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.31.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.26.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.30.1': + '@rollup/rollup-linux-riscv64-gnu@4.31.0': optional: true '@rollup/rollup-linux-s390x-gnu@4.26.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.30.1': + '@rollup/rollup-linux-s390x-gnu@4.31.0': optional: true '@rollup/rollup-linux-x64-gnu@4.26.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.30.1': + '@rollup/rollup-linux-x64-gnu@4.31.0': optional: true '@rollup/rollup-linux-x64-musl@4.26.0': optional: true - '@rollup/rollup-linux-x64-musl@4.30.1': + '@rollup/rollup-linux-x64-musl@4.31.0': optional: true '@rollup/rollup-win32-arm64-msvc@4.26.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.30.1': + '@rollup/rollup-win32-arm64-msvc@4.31.0': optional: true '@rollup/rollup-win32-ia32-msvc@4.26.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.30.1': + '@rollup/rollup-win32-ia32-msvc@4.31.0': optional: true '@rollup/rollup-win32-x64-msvc@4.26.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.30.1': + '@rollup/rollup-win32-x64-msvc@4.31.0': optional: true '@rushstack/eslint-patch@1.10.5': {} @@ -14692,10 +14726,10 @@ snapshots: unhead: 1.11.15 vue: 3.5.13(typescript@5.6.1-rc) - '@vercel/nft@0.27.10(encoding@0.1.13)(rollup@4.30.1)': + '@vercel/nft@0.27.10(encoding@0.1.13)(rollup@4.31.0)': dependencies: '@mapbox/node-pre-gyp': 2.0.0-rc.0(encoding@0.1.13) - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) acorn: 8.14.0 acorn-import-attributes: 1.9.5(acorn@8.14.0) async-sema: 3.1.1 @@ -14825,10 +14859,10 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.0.8 - '@vue-macros/common@1.15.1(rollup@4.30.1)(vue@3.5.13(typescript@5.5.3))': + '@vue-macros/common@1.15.1(rollup@4.31.0)(vue@3.5.13(typescript@5.5.3))': dependencies: '@babel/types': 7.26.3 - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) '@vue/compiler-sfc': 3.5.13 ast-kit: 1.3.2 local-pkg: 0.5.1 @@ -14838,10 +14872,10 @@ snapshots: transitivePeerDependencies: - rollup - '@vue-macros/common@1.15.1(rollup@4.30.1)(vue@3.5.13(typescript@5.6.1-rc))': + '@vue-macros/common@1.15.1(rollup@4.31.0)(vue@3.5.13(typescript@5.6.1-rc))': dependencies: '@babel/types': 7.26.3 - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) '@vue/compiler-sfc': 3.5.13 ast-kit: 1.3.2 local-pkg: 0.5.1 @@ -14922,14 +14956,14 @@ snapshots: dependencies: '@vue/devtools-kit': 7.7.0 - '@vue/devtools-core@7.6.8(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3))': + '@vue/devtools-core@7.6.8(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))(vue@3.5.13(typescript@5.5.3))': dependencies: '@vue/devtools-kit': 7.7.0 '@vue/devtools-shared': 7.7.0 mitt: 3.0.1 nanoid: 5.0.9 pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + vite-hot-client: 0.2.4(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) vue: 3.5.13(typescript@5.5.3) transitivePeerDependencies: - vite @@ -14946,6 +14980,18 @@ snapshots: transitivePeerDependencies: - vite + '@vue/devtools-core@7.6.8(vue@3.5.13(typescript@5.6.1-rc))': + dependencies: + '@vue/devtools-kit': 7.7.0 + '@vue/devtools-shared': 7.7.0 + mitt: 3.0.1 + nanoid: 5.0.9 + pathe: 1.1.2 + vite-hot-client: 0.2.4(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + vue: 3.5.13(typescript@5.6.1-rc) + transitivePeerDependencies: + - vite + '@vue/devtools-core@7.7.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3))': dependencies: '@vue/devtools-kit': 7.7.0 @@ -17341,9 +17387,9 @@ snapshots: import-meta-resolve@4.1.0: {} - impound@0.2.0(rollup@4.30.1): + impound@0.2.0(rollup@4.31.0): dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) mlly: 1.7.3 pathe: 1.1.2 unenv: 1.10.0 @@ -18306,16 +18352,16 @@ snapshots: dependencies: '@cloudflare/kv-asset-handler': 0.3.4 '@netlify/functions': 2.8.2 - '@rollup/plugin-alias': 5.1.1(rollup@4.30.1) - '@rollup/plugin-commonjs': 28.0.2(rollup@4.30.1) - '@rollup/plugin-inject': 5.0.5(rollup@4.30.1) - '@rollup/plugin-json': 6.1.0(rollup@4.30.1) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.30.1) - '@rollup/plugin-replace': 6.0.2(rollup@4.30.1) - '@rollup/plugin-terser': 0.4.4(rollup@4.30.1) - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/plugin-alias': 5.1.1(rollup@4.31.0) + '@rollup/plugin-commonjs': 28.0.2(rollup@4.31.0) + '@rollup/plugin-inject': 5.0.5(rollup@4.31.0) + '@rollup/plugin-json': 6.1.0(rollup@4.31.0) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.31.0) + '@rollup/plugin-replace': 6.0.2(rollup@4.31.0) + '@rollup/plugin-terser': 0.4.4(rollup@4.31.0) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) '@types/http-proxy': 1.17.15 - '@vercel/nft': 0.27.10(encoding@0.1.13)(rollup@4.30.1) + '@vercel/nft': 0.27.10(encoding@0.1.13)(rollup@4.31.0) archiver: 7.0.1 c12: 2.0.1(magicast@0.3.5) chokidar: 3.6.0 @@ -18357,8 +18403,8 @@ snapshots: pkg-types: 1.3.0 pretty-bytes: 6.1.1 radix3: 1.1.2 - rollup: 4.30.1 - rollup-plugin-visualizer: 5.14.0(rollup@4.30.1) + rollup: 4.31.0 + rollup-plugin-visualizer: 5.14.0(rollup@4.31.0) scule: 1.3.0 semver: 7.6.3 serve-placeholder: 2.0.2 @@ -18368,7 +18414,7 @@ snapshots: uncrypto: 0.1.3 unctx: 2.4.1 unenv: 1.10.0 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) unstorage: 1.14.4(db0@0.2.1)(ioredis@5.4.2) untyped: 1.5.2 unwasm: 0.3.9 @@ -18403,16 +18449,16 @@ snapshots: dependencies: '@cloudflare/kv-asset-handler': 0.3.4 '@netlify/functions': 2.8.2 - '@rollup/plugin-alias': 5.1.1(rollup@4.30.1) - '@rollup/plugin-commonjs': 28.0.2(rollup@4.30.1) - '@rollup/plugin-inject': 5.0.5(rollup@4.30.1) - '@rollup/plugin-json': 6.1.0(rollup@4.30.1) - '@rollup/plugin-node-resolve': 15.3.1(rollup@4.30.1) - '@rollup/plugin-replace': 6.0.2(rollup@4.30.1) - '@rollup/plugin-terser': 0.4.4(rollup@4.30.1) - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/plugin-alias': 5.1.1(rollup@4.31.0) + '@rollup/plugin-commonjs': 28.0.2(rollup@4.31.0) + '@rollup/plugin-inject': 5.0.5(rollup@4.31.0) + '@rollup/plugin-json': 6.1.0(rollup@4.31.0) + '@rollup/plugin-node-resolve': 15.3.1(rollup@4.31.0) + '@rollup/plugin-replace': 6.0.2(rollup@4.31.0) + '@rollup/plugin-terser': 0.4.4(rollup@4.31.0) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) '@types/http-proxy': 1.17.15 - '@vercel/nft': 0.27.10(encoding@0.1.13)(rollup@4.30.1) + '@vercel/nft': 0.27.10(encoding@0.1.13)(rollup@4.31.0) archiver: 7.0.1 c12: 2.0.1(magicast@0.3.5) chokidar: 3.6.0 @@ -18454,8 +18500,8 @@ snapshots: pkg-types: 1.3.0 pretty-bytes: 6.1.1 radix3: 1.1.2 - rollup: 4.30.1 - rollup-plugin-visualizer: 5.14.0(rollup@4.30.1) + rollup: 4.31.0 + rollup-plugin-visualizer: 5.14.0(rollup@4.31.0) scule: 1.3.0 semver: 7.6.3 serve-placeholder: 2.0.2 @@ -18465,7 +18511,7 @@ snapshots: uncrypto: 0.1.3 unctx: 2.4.1 unenv: 1.10.0 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) unstorage: 1.14.4(db0@0.2.1)(ioredis@5.4.2) untyped: 1.5.2 unwasm: 0.3.9 @@ -18640,14 +18686,14 @@ snapshots: nuxi@3.19.1: {} - nuxt@3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0): + nuxt@3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))(yaml@2.7.0): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/devtools': 1.7.0(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))(vue@3.5.13(typescript@5.5.3)) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) '@nuxt/schema': 3.15.1 - '@nuxt/telemetry': 2.6.4(magicast@0.3.5)(rollup@4.30.1) - '@nuxt/vite-builder': 3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vue@3.5.13(typescript@5.5.3))(yaml@2.7.0) + '@nuxt/telemetry': 2.6.4(magicast@0.3.5)(rollup@4.31.0) + '@nuxt/vite-builder': 3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.5.3)(vue@3.5.13(typescript@5.5.3))(yaml@2.7.0) '@unhead/dom': 1.11.15 '@unhead/shared': 1.11.15 '@unhead/ssr': 1.11.15 @@ -18670,7 +18716,7 @@ snapshots: h3: 1.13.1 hookable: 5.5.3 ignore: 7.0.0 - impound: 0.2.0(rollup@4.30.1) + impound: 0.2.0(rollup@4.31.0) jiti: 2.4.2 klona: 2.0.6 knitwork: 1.2.0 @@ -18697,9 +18743,9 @@ snapshots: unctx: 2.4.1 unenv: 1.10.0 unhead: 1.11.15 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) unplugin: 2.1.2 - unplugin-vue-router: 0.10.9(rollup@4.30.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3)) + unplugin-vue-router: 0.10.9(rollup@4.31.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3)) unstorage: 1.14.4(db0@0.2.1)(ioredis@5.4.2) untyped: 1.5.2 vue: 3.5.13(typescript@5.5.3) @@ -18761,14 +18807,14 @@ snapshots: - xml2js - yaml - nuxt@3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0): + nuxt@3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(yaml@2.7.0): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc)) - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/devtools': 1.7.0(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.6.1-rc)) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) '@nuxt/schema': 3.15.1 - '@nuxt/telemetry': 2.6.4(magicast@0.3.5)(rollup@4.30.1) - '@nuxt/vite-builder': 3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vue@3.5.13(typescript@5.6.1-rc))(yaml@2.7.0) + '@nuxt/telemetry': 2.6.4(magicast@0.3.5)(rollup@4.31.0) + '@nuxt/vite-builder': 3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vue@3.5.13(typescript@5.6.1-rc))(yaml@2.7.0) '@unhead/dom': 1.11.15 '@unhead/shared': 1.11.15 '@unhead/ssr': 1.11.15 @@ -18791,7 +18837,7 @@ snapshots: h3: 1.13.1 hookable: 5.5.3 ignore: 7.0.0 - impound: 0.2.0(rollup@4.30.1) + impound: 0.2.0(rollup@4.31.0) jiti: 2.4.2 klona: 2.0.6 knitwork: 1.2.0 @@ -18818,9 +18864,9 @@ snapshots: unctx: 2.4.1 unenv: 1.10.0 unhead: 1.11.15 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) unplugin: 2.1.2 - unplugin-vue-router: 0.10.9(rollup@4.30.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.1-rc)))(vue@3.5.13(typescript@5.6.1-rc)) + unplugin-vue-router: 0.10.9(rollup@4.31.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.1-rc)))(vue@3.5.13(typescript@5.6.1-rc)) unstorage: 1.14.4(db0@0.2.1)(ioredis@5.4.2) untyped: 1.5.2 vue: 3.5.13(typescript@5.6.1-rc) @@ -18882,14 +18928,14 @@ snapshots: - xml2js - yaml - nuxt@3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(yaml@2.7.0): + nuxt@3.15.1(@parcel/watcher@2.5.0)(@types/node@22.10.5)(db0@0.2.1)(encoding@0.1.13)(eslint@9.17.0(jiti@2.4.2))(ioredis@5.4.2)(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(yaml@2.7.0): dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.30.1)(vue@3.5.13(typescript@5.6.1-rc)) - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/devtools': 1.7.0(rollup@4.31.0)(vue@3.5.13(typescript@5.6.1-rc)) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) '@nuxt/schema': 3.15.1 - '@nuxt/telemetry': 2.6.4(magicast@0.3.5)(rollup@4.30.1) - '@nuxt/vite-builder': 3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vue@3.5.13(typescript@5.6.1-rc))(yaml@2.7.0) + '@nuxt/telemetry': 2.6.4(magicast@0.3.5)(rollup@4.31.0) + '@nuxt/vite-builder': 3.15.1(@types/node@22.10.5)(eslint@9.17.0(jiti@2.4.2))(less@4.2.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vue@3.5.13(typescript@5.6.1-rc))(yaml@2.7.0) '@unhead/dom': 1.11.15 '@unhead/shared': 1.11.15 '@unhead/ssr': 1.11.15 @@ -18912,7 +18958,7 @@ snapshots: h3: 1.13.1 hookable: 5.5.3 ignore: 7.0.0 - impound: 0.2.0(rollup@4.30.1) + impound: 0.2.0(rollup@4.31.0) jiti: 2.4.2 klona: 2.0.6 knitwork: 1.2.0 @@ -18939,9 +18985,9 @@ snapshots: unctx: 2.4.1 unenv: 1.10.0 unhead: 1.11.15 - unimport: 3.14.5(rollup@4.30.1) + unimport: 3.14.5(rollup@4.31.0) unplugin: 2.1.2 - unplugin-vue-router: 0.10.9(rollup@4.30.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.1-rc)))(vue@3.5.13(typescript@5.6.1-rc)) + unplugin-vue-router: 0.10.9(rollup@4.31.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.1-rc)))(vue@3.5.13(typescript@5.6.1-rc)) unstorage: 1.14.4(db0@0.2.1)(ioredis@5.4.2) untyped: 1.5.2 vue: 3.5.13(typescript@5.6.1-rc) @@ -20037,14 +20083,22 @@ snapshots: dependencies: glob: 10.4.3 - rollup-plugin-visualizer@5.14.0(rollup@4.30.1): + rollup-plugin-dts@6.1.1(rollup@4.31.0)(typescript@5.5.3): + dependencies: + magic-string: 0.30.17 + rollup: 4.31.0 + typescript: 5.5.3 + optionalDependencies: + '@babel/code-frame': 7.26.2 + + rollup-plugin-visualizer@5.14.0(rollup@4.31.0): dependencies: open: 8.4.2 picomatch: 4.0.2 source-map: 0.7.4 yargs: 17.7.2 optionalDependencies: - rollup: 4.30.1 + rollup: 4.31.0 rollup@4.26.0: dependencies: @@ -20070,29 +20124,29 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.26.0 fsevents: 2.3.3 - rollup@4.30.1: + rollup@4.31.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.30.1 - '@rollup/rollup-android-arm64': 4.30.1 - '@rollup/rollup-darwin-arm64': 4.30.1 - '@rollup/rollup-darwin-x64': 4.30.1 - '@rollup/rollup-freebsd-arm64': 4.30.1 - '@rollup/rollup-freebsd-x64': 4.30.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.30.1 - '@rollup/rollup-linux-arm-musleabihf': 4.30.1 - '@rollup/rollup-linux-arm64-gnu': 4.30.1 - '@rollup/rollup-linux-arm64-musl': 4.30.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.30.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.30.1 - '@rollup/rollup-linux-riscv64-gnu': 4.30.1 - '@rollup/rollup-linux-s390x-gnu': 4.30.1 - '@rollup/rollup-linux-x64-gnu': 4.30.1 - '@rollup/rollup-linux-x64-musl': 4.30.1 - '@rollup/rollup-win32-arm64-msvc': 4.30.1 - '@rollup/rollup-win32-ia32-msvc': 4.30.1 - '@rollup/rollup-win32-x64-msvc': 4.30.1 + '@rollup/rollup-android-arm-eabi': 4.31.0 + '@rollup/rollup-android-arm64': 4.31.0 + '@rollup/rollup-darwin-arm64': 4.31.0 + '@rollup/rollup-darwin-x64': 4.31.0 + '@rollup/rollup-freebsd-arm64': 4.31.0 + '@rollup/rollup-freebsd-x64': 4.31.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.31.0 + '@rollup/rollup-linux-arm-musleabihf': 4.31.0 + '@rollup/rollup-linux-arm64-gnu': 4.31.0 + '@rollup/rollup-linux-arm64-musl': 4.31.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.31.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.31.0 + '@rollup/rollup-linux-riscv64-gnu': 4.31.0 + '@rollup/rollup-linux-s390x-gnu': 4.31.0 + '@rollup/rollup-linux-x64-gnu': 4.31.0 + '@rollup/rollup-linux-x64-musl': 4.31.0 + '@rollup/rollup-win32-arm64-msvc': 4.31.0 + '@rollup/rollup-win32-ia32-msvc': 4.31.0 + '@rollup/rollup-win32-x64-msvc': 4.31.0 fsevents: 2.3.3 rrweb-cssom@0.6.0: {} @@ -21035,7 +21089,7 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.4.49)(yaml@2.7.0) resolve-from: 5.0.0 - rollup: 4.30.1 + rollup: 4.31.0 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 @@ -21153,9 +21207,9 @@ snapshots: unicorn-magic@0.3.0: {} - unimport@3.14.5(rollup@4.30.1): + unimport@3.14.5(rollup@4.31.0): dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) acorn: 8.14.0 escape-string-regexp: 5.0.0 estree-walker: 3.0.3 @@ -21211,11 +21265,11 @@ snapshots: unpipe@1.0.0: {} - unplugin-vue-router@0.10.9(rollup@4.30.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3)): + unplugin-vue-router@0.10.9(rollup@4.31.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.5.3)))(vue@3.5.13(typescript@5.5.3)): dependencies: '@babel/types': 7.26.3 - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) - '@vue-macros/common': 1.15.1(rollup@4.30.1)(vue@3.5.13(typescript@5.5.3)) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) + '@vue-macros/common': 1.15.1(rollup@4.31.0)(vue@3.5.13(typescript@5.5.3)) ast-walker-scope: 0.6.2 chokidar: 3.6.0 fast-glob: 3.3.3 @@ -21233,11 +21287,11 @@ snapshots: - rollup - vue - unplugin-vue-router@0.10.9(rollup@4.30.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.1-rc)))(vue@3.5.13(typescript@5.6.1-rc)): + unplugin-vue-router@0.10.9(rollup@4.31.0)(vue-router@4.5.0(vue@3.5.13(typescript@5.6.1-rc)))(vue@3.5.13(typescript@5.6.1-rc)): dependencies: '@babel/types': 7.26.3 - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) - '@vue-macros/common': 1.15.1(rollup@4.30.1)(vue@3.5.13(typescript@5.6.1-rc)) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) + '@vue-macros/common': 1.15.1(rollup@4.31.0)(vue@3.5.13(typescript@5.6.1-rc)) ast-walker-scope: 0.6.2 chokidar: 3.6.0 fast-glob: 3.3.3 @@ -21379,6 +21433,10 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 + vite-hot-client@0.2.4(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)): + dependencies: + vite: 5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + vite-hot-client@0.2.4(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)): dependencies: vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) @@ -21463,10 +21521,28 @@ snapshots: optionator: 0.9.4 typescript: 5.6.1-rc - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1))(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)): + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0))(rollup@4.31.0)(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)): dependencies: '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.4(rollup@4.30.1) + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) + debug: 4.4.0(supports-color@9.4.0) + error-stack-parser-es: 0.1.5 + fs-extra: 11.2.0 + open: 10.1.0 + perfect-debounce: 1.0.0 + picocolors: 1.1.1 + sirv: 3.0.0 + vite: 5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + optionalDependencies: + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) + transitivePeerDependencies: + - rollup + - supports-color + + vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0))(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)): + dependencies: + '@antfu/utils': 0.7.10 + '@rollup/pluginutils': 5.1.4(rollup@4.31.0) debug: 4.4.0(supports-color@9.4.0) error-stack-parser-es: 0.1.5 fs-extra: 11.2.0 @@ -21476,12 +21552,12 @@ snapshots: sirv: 3.0.0 vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) optionalDependencies: - '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.30.1) + '@nuxt/kit': 3.15.1(magicast@0.3.5)(rollup@4.31.0) transitivePeerDependencies: - rollup - supports-color - vite-plugin-vue-devtools@7.7.0(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)): + vite-plugin-vue-devtools@7.7.0(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)): dependencies: '@vue/devtools-core': 7.7.0(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0))(vue@3.5.13(typescript@5.5.3)) '@vue/devtools-kit': 7.7.0 @@ -21489,7 +21565,7 @@ snapshots: execa: 9.5.2 sirv: 3.0.0 vite: 6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.30.1))(rollup@4.30.1)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) + vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.1(magicast@0.3.5)(rollup@4.31.0))(rollup@4.31.0)(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) vite-plugin-vue-inspector: 5.3.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)) transitivePeerDependencies: - '@nuxt/kit' @@ -21497,6 +21573,21 @@ snapshots: - supports-color - vue + vite-plugin-vue-inspector@5.3.1(vite@5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-proposal-decorators': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-transform-typescript': 7.26.3(@babel/core@7.26.0) + '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.0) + '@vue/compiler-dom': 3.5.13 + kolorist: 1.8.0 + magic-string: 0.30.17 + vite: 5.4.11(@types/node@22.10.5)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + transitivePeerDependencies: + - supports-color + vite-plugin-vue-inspector@5.3.1(vite@6.0.7(@types/node@22.10.5)(jiti@2.4.2)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)(yaml@2.7.0)): dependencies: '@babel/core': 7.26.0 @@ -21516,7 +21607,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.30.1 + rollup: 4.31.0 optionalDependencies: '@types/node': 22.10.5 fsevents: 2.3.3 @@ -21528,7 +21619,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.49 - rollup: 4.30.1 + rollup: 4.31.0 optionalDependencies: '@types/node': 22.10.5 fsevents: 2.3.3 @@ -21540,7 +21631,7 @@ snapshots: dependencies: esbuild: 0.24.2 postcss: 8.4.49 - rollup: 4.30.1 + rollup: 4.31.0 optionalDependencies: '@types/node': 22.10.5 fsevents: 2.3.3 @@ -21602,9 +21693,9 @@ snapshots: - typescript - universal-cookie - vitest-environment-nuxt@1.0.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)): + vitest-environment-nuxt@1.0.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)): dependencies: - '@nuxt/test-utils': 3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.30.1)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + '@nuxt/test-utils': 3.15.1(@types/node@22.10.5)(@vue/test-utils@2.4.6)(jsdom@24.1.0)(less@4.2.0)(magicast@0.3.5)(rollup@4.31.0)(sass@1.80.7)(terser@5.36.0)(typescript@5.6.1-rc)(vitest@1.6.0(@types/node@22.10.5)(jsdom@24.1.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals'