diff --git a/.changeset/refactor-pinia-colada-query.md b/.changeset/refactor-pinia-colada-query.md new file mode 100644 index 0000000000..8e7d8c3b37 --- /dev/null +++ b/.changeset/refactor-pinia-colada-query.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": patch +--- + +feat(pinia-colada): query options use `defineQueryOptions` diff --git a/docs/data/people.ts b/docs/data/people.ts index 662cb18a73..dc7e75b306 100644 --- a/docs/data/people.ts +++ b/docs/data/people.ts @@ -3,6 +3,11 @@ type Person = { name: string; }; +export const dmitriyBrolnickij: Person = { + github: 'https://github.com/brolnickij', + name: 'Dmitriy Brolnickij', +}; + export const jacobCohen: Person = { github: 'https://github.com/jacobinu', name: 'Jacob Cohen', diff --git a/docs/openapi-ts/clients/ofetch.md b/docs/openapi-ts/clients/ofetch.md index 4798a0b1b0..14bb3ed01a 100644 --- a/docs/openapi-ts/clients/ofetch.md +++ b/docs/openapi-ts/clients/ofetch.md @@ -3,6 +3,11 @@ title: OFetch Client description: Generate a type-safe ofetch client from OpenAPI with the ofetch client for openapi-ts. Fully compatible with validators, transformers, and all core features. --- + + # OFetch ### About @@ -11,6 +16,10 @@ description: Generate a type-safe ofetch client from OpenAPI with the ofetch cli The `ofetch` client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features. +### Collaborators + + + ## Features - seamless integration with `@hey-api/openapi-ts` ecosystem diff --git a/docs/openapi-ts/plugins/pinia-colada.md b/docs/openapi-ts/plugins/pinia-colada.md index 5ec2a00cc3..6c3c94cdb5 100644 --- a/docs/openapi-ts/plugins/pinia-colada.md +++ b/docs/openapi-ts/plugins/pinia-colada.md @@ -7,7 +7,7 @@ description: Generate Pinia Colada v0 functions and query keys from OpenAPI with import AuthorsList from '@components/AuthorsList.vue'; import Heading from '@components/Heading.vue'; import VersionLabel from '@components/VersionLabel.vue'; -import { joshHemphill, sebastiaanWouters } from '@data/people.js'; +import { joshHemphill, sebastiaanWouters, dmitriyBrolnickij } from '@data/people.js'; @@ -23,7 +23,7 @@ The Pinia Colada plugin for Hey API generates functions and query keys from your ### Collaborators - + ## Features @@ -65,13 +65,11 @@ Queries are generated from [query operations](/openapi-ts/configuration/parser#h ::: code-group ```ts [example] -const query = useQuery({ - ...getPetByIdQuery({ - path: { - petId: 1, - }, - }), -}); +const query = useQuery(getPetByIdQuery, () => ({ + path: { + petId: 1, + }, +})); ``` ```js [config] diff --git a/examples/openapi-ts-pinia-colada/openapi-ts.config.ts b/examples/openapi-ts-pinia-colada/openapi-ts.config.ts index 7a00915a40..a231ae4d61 100644 --- a/examples/openapi-ts-pinia-colada/openapi-ts.config.ts +++ b/examples/openapi-ts-pinia-colada/openapi-ts.config.ts @@ -18,7 +18,8 @@ export default defineConfig({ }, { exportFromIndex: true, - name: '@pinia/colada' + name: '@pinia/colada', + queryKeys: false } ] }) diff --git a/examples/openapi-ts-pinia-colada/src/client/@pinia/colada.gen.ts b/examples/openapi-ts-pinia-colada/src/client/@pinia/colada.gen.ts index 21f0097478..1564abf607 100644 --- a/examples/openapi-ts-pinia-colada/src/client/@pinia/colada.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/@pinia/colada.gen.ts @@ -1,5 +1,9 @@ // This file is auto-generated by @hey-api/openapi-ts +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada' + +import { client } from '../client.gen' +import { serializeQueryKeyValue } from '../core/queryKeySerializer.gen' import { addPet, createUser, @@ -24,8 +28,11 @@ import { } from '../sdk.gen' import type { AddPetData, + AddPetResponse, CreateUserData, + CreateUserResponse, CreateUsersWithListInputData, + CreateUsersWithListInputResponse, DeleteOrderData, DeletePetData, DeleteUserData, @@ -38,19 +45,29 @@ import type { LoginUserData, LogoutUserData, PlaceOrderData, + PlaceOrderResponse, UpdatePetData, + UpdatePetResponse, UpdatePetWithFormData, + UpdatePetWithFormResponse, UpdateUserData, - UploadFileData + UploadFileData, + UploadFileResponse } from '../types.gen' /** * Add a new pet to the store. * Add a new pet to the store. */ -export const addPetMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await addPet(options) +export const addPetMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await addPet({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -59,52 +76,106 @@ export const addPetMutation = () => ({ * Update an existing pet. * Update an existing pet by Id. */ -export const updatePetMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await updatePet(options) +export const updatePetMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await updatePet({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) +export type QueryKey = [ + Pick & { + _id: string + baseUrl?: _JSONValue + body?: _JSONValue + query?: _JSONValue + tags?: _JSONValue + } +] + +const createQueryKey = ( + id: string, + options?: TOptions, + tags?: ReadonlyArray +): [QueryKey[0]] => { + const params: QueryKey[0] = { + _id: id, + baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl + } as QueryKey[0] + if (tags) { + params.tags = tags as unknown as _JSONValue + } + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body) + if (normalizedBody !== undefined) { + params.body = normalizedBody + } + } + if (options?.path) { + params.path = options.path + } + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query) + if (normalizedQuery !== undefined) { + params.query = normalizedQuery + } + } + return [params] +} + /** * Finds Pets by status. * Multiple status values can be provided with comma separated strings. */ -export const findPetsByStatusQuery = (options: Options) => ({ - key: ['findPetsByStatus', options?.path], - query: async (context: { signal: AbortSignal }) => { - const { data } = await findPetsByStatus({ - ...options, - signal: context.signal, - throwOnError: true - }) - return data - } -}) +export const findPetsByStatusQuery = defineQueryOptions( + (options: Options) => ({ + key: createQueryKey('findPetsByStatus', options), + query: async (context) => { + const { data } = await findPetsByStatus({ + ...options, + ...context, + throwOnError: true + }) + return data + } + }) +) /** * Finds Pets by tags. * Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. */ -export const findPetsByTagsQuery = (options: Options) => ({ - key: ['findPetsByTags', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const findPetsByTagsQuery = defineQueryOptions((options: Options) => ({ + key: createQueryKey('findPetsByTags', options), + query: async (context) => { const { data } = await findPetsByTags({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Deletes a pet. * Delete a pet. */ -export const deletePetMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await deletePet(options) +export const deletePetMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await deletePet({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -113,25 +184,31 @@ export const deletePetMutation = () => ({ * Find pet by ID. * Returns a single pet. */ -export const getPetByIdQuery = (options: Options) => ({ - key: ['getPetById', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const getPetByIdQuery = defineQueryOptions((options: Options) => ({ + key: createQueryKey('getPetById', options), + query: async (context) => { const { data } = await getPetById({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Updates a pet in the store with form data. * Updates a pet resource based on the form data. */ -export const updatePetWithFormMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await updatePetWithForm(options) +export const updatePetWithFormMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await updatePetWithForm({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -140,9 +217,15 @@ export const updatePetWithFormMutation = () => ({ * Uploads an image. * Upload image of the pet. */ -export const uploadFileMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await uploadFile(options) +export const uploadFileMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await uploadFile({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -151,25 +234,31 @@ export const uploadFileMutation = () => ({ * Returns pet inventories by status. * Returns a map of status codes to quantities. */ -export const getInventoryQuery = (options?: Options) => ({ - key: ['getInventory', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const getInventoryQuery = defineQueryOptions((options?: Options) => ({ + key: createQueryKey('getInventory', options), + query: async (context) => { const { data } = await getInventory({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Place an order for a pet. * Place a new order in the store. */ -export const placeOrderMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await placeOrder(options) +export const placeOrderMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await placeOrder({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -178,9 +267,15 @@ export const placeOrderMutation = () => ({ * Delete purchase order by identifier. * For valid response try integer IDs with value < 1000. Anything above 1000 or non-integers will generate API errors. */ -export const deleteOrderMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await deleteOrder(options) +export const deleteOrderMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await deleteOrder({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -189,25 +284,31 @@ export const deleteOrderMutation = () => ({ * Find purchase order by ID. * For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions. */ -export const getOrderByIdQuery = (options: Options) => ({ - key: ['getOrderById', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const getOrderByIdQuery = defineQueryOptions((options: Options) => ({ + key: createQueryKey('getOrderById', options), + query: async (context) => { const { data } = await getOrderById({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Create user. * This can only be done by the logged in user. */ -export const createUserMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await createUser(options) +export const createUserMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await createUser({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -216,9 +317,19 @@ export const createUserMutation = () => ({ * Creates list of users with given input array. * Creates list of users with given input array. */ -export const createUsersWithListInputMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await createUsersWithListInput(options) +export const createUsersWithListInputMutation = ( + options?: Partial> +): UseMutationOptions< + CreateUsersWithListInputResponse, + Options, + Error +> => ({ + mutation: async (fnOptions) => { + const { data } = await createUsersWithListInput({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -227,41 +338,47 @@ export const createUsersWithListInputMutation = () => ({ * Logs user into the system. * Log into the system. */ -export const loginUserQuery = (options?: Options) => ({ - key: ['loginUser', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const loginUserQuery = defineQueryOptions((options?: Options) => ({ + key: createQueryKey('loginUser', options), + query: async (context) => { const { data } = await loginUser({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Logs out current logged in user session. * Log user out of the system. */ -export const logoutUserQuery = (options?: Options) => ({ - key: ['logoutUser', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const logoutUserQuery = defineQueryOptions((options?: Options) => ({ + key: createQueryKey('logoutUser', options), + query: async (context) => { const { data } = await logoutUser({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Delete user resource. * This can only be done by the logged in user. */ -export const deleteUserMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await deleteUser(options) +export const deleteUserMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await deleteUser({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) @@ -270,25 +387,31 @@ export const deleteUserMutation = () => ({ * Get user by user name. * Get user detail based on username. */ -export const getUserByNameQuery = (options: Options) => ({ - key: ['getUserByName', options?.path], - query: async (context: { signal: AbortSignal }) => { +export const getUserByNameQuery = defineQueryOptions((options: Options) => ({ + key: createQueryKey('getUserByName', options), + query: async (context) => { const { data } = await getUserByName({ ...options, - signal: context.signal, + ...context, throwOnError: true }) return data } -}) +})) /** * Update user resource. * This can only be done by the logged in user. */ -export const updateUserMutation = () => ({ - mutation: async (options: Options) => { - const { data } = await updateUser(options) +export const updateUserMutation = ( + options?: Partial> +): UseMutationOptions, Error> => ({ + mutation: async (fnOptions) => { + const { data } = await updateUser({ + ...options, + ...fnOptions, + throwOnError: true + }) return data } }) diff --git a/examples/openapi-ts-pinia-colada/src/client/client.gen.ts b/examples/openapi-ts-pinia-colada/src/client/client.gen.ts index 1984dc18c0..3431b130d4 100644 --- a/examples/openapi-ts-pinia-colada/src/client/client.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/client.gen.ts @@ -1,12 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import { - type ClientOptions as DefaultClientOptions, - type Config, - createClient, - createConfig -} from './client' -import type { ClientOptions } from './types.gen' +import { type ClientOptions, type Config, createClient, createConfig } from './client' +import type { ClientOptions as ClientOptions2 } from './types.gen' /** * The `createClientConfig()` function will be called on client initialization @@ -16,12 +11,12 @@ import type { ClientOptions } from './types.gen' * `setConfig()`. This is useful for example if you're using Next.js * to ensure your client always has the correct values. */ -export type CreateClientConfig = ( - override?: Config -) => Config & T> +export type CreateClientConfig = ( + override?: Config +) => Config & T> export const client = createClient( - createConfig({ + createConfig({ baseUrl: 'https://petstore3.swagger.io/api/v3' }) ) diff --git a/examples/openapi-ts-pinia-colada/src/client/client/client.gen.ts b/examples/openapi-ts-pinia-colada/src/client/client/client.gen.ts index 65910541f0..4a4fc4648f 100644 --- a/examples/openapi-ts-pinia-colada/src/client/client/client.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/client/client.gen.ts @@ -2,6 +2,7 @@ import { createSseClient } from '../core/serverSentEvents.gen' import type { HttpMethod } from '../core/types.gen' +import { getValidRequestBody } from '../core/utils.gen' import type { Client, Config, RequestOptions, ResolvedRequestOptions } from './types.gen' import { buildUrl, @@ -50,12 +51,12 @@ export const createClient = (config: Config = {}): Client => { await opts.requestValidator(opts) } - if (opts.body && opts.bodySerializer) { + if (opts.body !== undefined && opts.bodySerializer) { opts.serializedBody = opts.bodySerializer(opts.body) } // remove Content-Type header if body is empty to avoid sending invalid requests - if (opts.serializedBody === undefined || opts.serializedBody === '') { + if (opts.body === undefined || opts.serializedBody === '') { opts.headers.delete('Content-Type') } @@ -70,12 +71,12 @@ export const createClient = (config: Config = {}): Client => { const requestInit: ReqInit = { redirect: 'follow', ...opts, - body: opts.serializedBody + body: getValidRequestBody(opts) } let request = new Request(url, requestInit) - for (const fn of interceptors.request._fns) { + for (const fn of interceptors.request.fns) { if (fn) { request = await fn(request, opts) } @@ -86,7 +87,7 @@ export const createClient = (config: Config = {}): Client => { const _fetch = opts.fetch! let response = await _fetch(request) - for (const fn of interceptors.response._fns) { + for (const fn of interceptors.response.fns) { if (fn) { response = await fn(response, request, opts) } @@ -98,20 +99,38 @@ export const createClient = (config: Config = {}): Client => { } if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json' + if (response.status === 204 || response.headers.get('Content-Length') === '0') { + let emptyData: any + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs]() + break + case 'formData': + emptyData = new FormData() + break + case 'stream': + emptyData = response.body + break + case 'json': + default: + emptyData = {} + break + } return opts.responseStyle === 'data' - ? {} + ? emptyData : { - data: {}, + data: emptyData, ...result } } - const parseAs = - (opts.parseAs === 'auto' - ? getParseAs(response.headers.get('Content-Type')) - : opts.parseAs) ?? 'json' - let data: any switch (parseAs) { case 'arrayBuffer': @@ -160,7 +179,7 @@ export const createClient = (config: Config = {}): Client => { const error = jsonError ?? textError let finalError = error - for (const fn of interceptors.error._fns) { + for (const fn of interceptors.error.fns) { if (fn) { finalError = (await fn(error, response, request, opts)) as string } @@ -191,6 +210,15 @@ export const createClient = (config: Config = {}): Client => { body: opts.body as BodyInit | null | undefined, headers: opts.headers as unknown as Record, method, + onRequest: async (url, init) => { + let request = new Request(url, init) + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts) + } + } + return request + }, url }) } diff --git a/examples/openapi-ts-pinia-colada/src/client/client/types.gen.ts b/examples/openapi-ts-pinia-colada/src/client/client/types.gen.ts index 273baf7860..b97f75e3fc 100644 --- a/examples/openapi-ts-pinia-colada/src/client/client/types.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/client/types.gen.ts @@ -20,7 +20,7 @@ export interface Config * * @default globalThis.fetch */ - fetch?: (request: Request) => ReturnType + fetch?: typeof fetch /** * Please don't use the Fetch client for Next.js applications. The `next` * options won't have any effect. diff --git a/examples/openapi-ts-pinia-colada/src/client/client/utils.gen.ts b/examples/openapi-ts-pinia-colada/src/client/client/utils.gen.ts index f701b3d78c..b42b5d9516 100644 --- a/examples/openapi-ts-pinia-colada/src/client/client/utils.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/client/utils.gen.ts @@ -176,16 +176,24 @@ export const mergeConfigs = (a: Config, b: Config): Config => { return config } +const headersEntries = (headers: Headers): Array<[string, string]> => { + const entries: Array<[string, string]> = [] + headers.forEach((value, key) => { + entries.push([key, value]) + }) + return entries +} + export const mergeHeaders = ( ...headers: Array['headers'] | undefined> ): Headers => { const mergedHeaders = new Headers() for (const header of headers) { - if (!header || typeof header !== 'object') { + if (!header) { continue } - const iterator = header instanceof Headers ? header.entries() : Object.entries(header) + const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header) for (const [key, value] of iterator) { if (value === null) { @@ -223,61 +231,58 @@ type ResInterceptor = ( ) => Res | Promise class Interceptors { - _fns: (Interceptor | null)[] + fns: Array = [] - constructor() { - this._fns = [] + clear(): void { + this.fns = [] } - clear() { - this._fns = [] - } - - getInterceptorIndex(id: number | Interceptor): number { - if (typeof id === 'number') { - return this._fns[id] ? id : -1 - } else { - return this._fns.indexOf(id) + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id) + if (this.fns[index]) { + this.fns[index] = null } } - exists(id: number | Interceptor) { + + exists(id: number | Interceptor): boolean { const index = this.getInterceptorIndex(id) - return !!this._fns[index] + return Boolean(this.fns[index]) } - eject(id: number | Interceptor) { - const index = this.getInterceptorIndex(id) - if (this._fns[index]) { - this._fns[index] = null + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1 } + return this.fns.indexOf(id) } - update(id: number | Interceptor, fn: Interceptor) { + update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false { const index = this.getInterceptorIndex(id) - if (this._fns[index]) { - this._fns[index] = fn + if (this.fns[index]) { + this.fns[index] = fn return id - } else { - return false } + return false } - use(fn: Interceptor) { - this._fns = [...this._fns, fn] - return this._fns.length - 1 + use(fn: Interceptor): number { + this.fns.push(fn) + return this.fns.length - 1 } } -// `createInterceptors()` response, meant for external use as it does not -// expose internals export interface Middleware { - error: Pick>, 'eject' | 'use'> - request: Pick>, 'eject' | 'use'> - response: Pick>, 'eject' | 'use'> + error: Interceptors> + request: Interceptors> + response: Interceptors> } -// do not add `Middleware` as return type so we can use _fns internally -export const createInterceptors = () => ({ +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ error: new Interceptors>(), request: new Interceptors>(), response: new Interceptors>() diff --git a/examples/openapi-ts-pinia-colada/src/client/core/queryKeySerializer.gen.ts b/examples/openapi-ts-pinia-colada/src/client/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..7e9d0d1ec8 --- /dev/null +++ b/examples/openapi-ts-pinia-colada/src/client/core/queryKeySerializer.gen.ts @@ -0,0 +1,117 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue } + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined + } + if (typeof value === 'bigint') { + return value.toString() + } + if (value instanceof Date) { + return value.toISOString() + } + return value +} + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer) + if (json === undefined) { + return undefined + } + return JSON.parse(json) as JsonValue + } catch { + return undefined + } +} + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false + } + const prototype = Object.getPrototypeOf(value as object) + return prototype === Object.prototype || prototype === null +} + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b)) + const result: Record = {} + + for (const [key, value] of entries) { + const existing = result[key] + if (existing === undefined) { + result[key] = value + continue + } + + if (Array.isArray(existing)) { + ;(existing as string[]).push(value) + } else { + result[key] = [existing, value] + } + } + + return result +} + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => { + if (value === null) { + return null + } + + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + return value + } + + if (value === undefined || typeof value === 'function' || typeof value === 'symbol') { + return undefined + } + + if (typeof value === 'bigint') { + return value.toString() + } + + if (value instanceof Date) { + return value.toISOString() + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value) + } + + if (typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams) { + return serializeSearchParams(value) + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value) + } + + return undefined +} diff --git a/examples/openapi-ts-pinia-colada/src/client/core/serverSentEvents.gen.ts b/examples/openapi-ts-pinia-colada/src/client/core/serverSentEvents.gen.ts index de07ebaf27..372e50cc26 100644 --- a/examples/openapi-ts-pinia-colada/src/client/core/serverSentEvents.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/core/serverSentEvents.gen.ts @@ -4,6 +4,17 @@ import type { Config } from './types.gen' export type ServerSentEventsOptions = Omit & Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise /** * Callback invoked when a network or parsing error occurs during streaming. * @@ -21,6 +32,7 @@ export type ServerSentEventsOptions = Omit) => void + serializedBody?: RequestInit['body'] /** * Default retry delay in milliseconds. * @@ -68,6 +80,7 @@ export type ServerSentEventsResult({ + onRequest, onSseError, onSseEvent, responseTransformer, @@ -103,7 +116,21 @@ export const createSseClient = ({ } try { - const response = await fetch(url, { ...options, headers, signal }) + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal + } + let request = new Request(url, requestInit) + if (onRequest) { + request = await onRequest(url, requestInit) + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch + const response = await _fetch(request) if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`) diff --git a/examples/openapi-ts-pinia-colada/src/client/core/utils.gen.ts b/examples/openapi-ts-pinia-colada/src/client/core/utils.gen.ts index b99c527aac..bd078be240 100644 --- a/examples/openapi-ts-pinia-colada/src/client/core/utils.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/core/utils.gen.ts @@ -1,6 +1,6 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { QuerySerializer } from './bodySerializer.gen' +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen' import { type ArraySeparatorStyle, serializeArrayParam, @@ -109,3 +109,32 @@ export const getUrl = ({ } return url } + +export function getValidRequestBody(options: { + body?: unknown + bodySerializer?: BodySerializer | null + serializedBody?: unknown +}) { + const hasBody = options.body !== undefined + const isSerializedBody = hasBody && options.bodySerializer + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== '' + + return hasSerializedBody ? options.serializedBody : null + } + + // not all clients implement a serializedBody property (i.e. client-axios) + return options.body !== '' ? options.body : null + } + + // plain/text body + if (hasBody) { + return options.body + } + + // no body was provided + return undefined +} diff --git a/examples/openapi-ts-pinia-colada/src/client/index.ts b/examples/openapi-ts-pinia-colada/src/client/index.ts index 2543e57505..b190962d22 100644 --- a/examples/openapi-ts-pinia-colada/src/client/index.ts +++ b/examples/openapi-ts-pinia-colada/src/client/index.ts @@ -1,4 +1,5 @@ // This file is auto-generated by @hey-api/openapi-ts + export * from './@pinia/colada.gen' export * from './sdk.gen' -export * from './types.gen' +export type * from './types.gen' diff --git a/examples/openapi-ts-pinia-colada/src/client/sdk.gen.ts b/examples/openapi-ts-pinia-colada/src/client/sdk.gen.ts index f373c7cf6f..37778ac7f8 100644 --- a/examples/openapi-ts-pinia-colada/src/client/sdk.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/sdk.gen.ts @@ -1,7 +1,7 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Client, Options as ClientOptions, TDataShape } from './client' -import { client as _heyApiClient } from './client.gen' +import type { Client, Options as Options2, TDataShape } from './client' +import { client } from './client.gen' import type { AddPetData, AddPetErrors, @@ -65,7 +65,7 @@ import type { export type Options< TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean -> = ClientOptions & { +> = Options2 & { /** * You can provide a client instance returned by `createClient()` instead of * individual options. This might be also useful if you want to implement a @@ -86,7 +86,7 @@ export type Options< export const addPet = ( options: Options ) => - (options.client ?? _heyApiClient).post({ + (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -108,7 +108,7 @@ export const addPet = ( export const updatePet = ( options: Options ) => - (options.client ?? _heyApiClient).put({ + (options.client ?? client).put({ security: [ { scheme: 'bearer', @@ -130,11 +130,7 @@ export const updatePet = ( export const findPetsByStatus = ( options: Options ) => - (options.client ?? _heyApiClient).get< - FindPetsByStatusResponses, - FindPetsByStatusErrors, - ThrowOnError - >({ + (options.client ?? client).get({ security: [ { scheme: 'bearer', @@ -152,11 +148,7 @@ export const findPetsByStatus = ( export const findPetsByTags = ( options: Options ) => - (options.client ?? _heyApiClient).get< - FindPetsByTagsResponses, - FindPetsByTagsErrors, - ThrowOnError - >({ + (options.client ?? client).get({ security: [ { scheme: 'bearer', @@ -174,7 +166,7 @@ export const findPetsByTags = ( export const deletePet = ( options: Options ) => - (options.client ?? _heyApiClient).delete({ + (options.client ?? client).delete({ security: [ { scheme: 'bearer', @@ -192,7 +184,7 @@ export const deletePet = ( export const getPetById = ( options: Options ) => - (options.client ?? _heyApiClient).get({ + (options.client ?? client).get({ security: [ { name: 'api_key', @@ -214,7 +206,7 @@ export const getPetById = ( export const updatePetWithForm = ( options: Options ) => - (options.client ?? _heyApiClient).post< + (options.client ?? client).post< UpdatePetWithFormResponses, UpdatePetWithFormErrors, ThrowOnError @@ -236,7 +228,7 @@ export const updatePetWithForm = ( export const uploadFile = ( options: Options ) => - (options.client ?? _heyApiClient).post({ + (options.client ?? client).post({ bodySerializer: null, security: [ { @@ -259,7 +251,7 @@ export const uploadFile = ( export const getInventory = ( options?: Options ) => - (options?.client ?? _heyApiClient).get({ + (options?.client ?? client).get({ security: [ { name: 'api_key', @@ -277,7 +269,7 @@ export const getInventory = ( export const placeOrder = ( options?: Options ) => - (options?.client ?? _heyApiClient).post({ + (options?.client ?? client).post({ url: '/store/order', ...options, headers: { @@ -293,7 +285,7 @@ export const placeOrder = ( export const deleteOrder = ( options: Options ) => - (options.client ?? _heyApiClient).delete({ + (options.client ?? client).delete({ url: '/store/order/{orderId}', ...options }) @@ -305,7 +297,7 @@ export const deleteOrder = ( export const getOrderById = ( options: Options ) => - (options.client ?? _heyApiClient).get({ + (options.client ?? client).get({ url: '/store/order/{orderId}', ...options }) @@ -317,7 +309,7 @@ export const getOrderById = ( export const createUser = ( options?: Options ) => - (options?.client ?? _heyApiClient).post({ + (options?.client ?? client).post({ url: '/user', ...options, headers: { @@ -333,7 +325,7 @@ export const createUser = ( export const createUsersWithListInput = ( options?: Options ) => - (options?.client ?? _heyApiClient).post< + (options?.client ?? client).post< CreateUsersWithListInputResponses, CreateUsersWithListInputErrors, ThrowOnError @@ -353,7 +345,7 @@ export const createUsersWithListInput = ( export const loginUser = ( options?: Options ) => - (options?.client ?? _heyApiClient).get({ + (options?.client ?? client).get({ url: '/user/login', ...options }) @@ -365,7 +357,7 @@ export const loginUser = ( export const logoutUser = ( options?: Options ) => - (options?.client ?? _heyApiClient).get({ + (options?.client ?? client).get({ url: '/user/logout', ...options }) @@ -377,7 +369,7 @@ export const logoutUser = ( export const deleteUser = ( options: Options ) => - (options.client ?? _heyApiClient).delete({ + (options.client ?? client).delete({ url: '/user/{username}', ...options }) @@ -389,7 +381,7 @@ export const deleteUser = ( export const getUserByName = ( options: Options ) => - (options.client ?? _heyApiClient).get({ + (options.client ?? client).get({ url: '/user/{username}', ...options }) @@ -401,7 +393,7 @@ export const getUserByName = ( export const updateUser = ( options: Options ) => - (options.client ?? _heyApiClient).put({ + (options.client ?? client).put({ url: '/user/{username}', ...options, headers: { diff --git a/examples/openapi-ts-pinia-colada/src/client/types.gen.ts b/examples/openapi-ts-pinia-colada/src/client/types.gen.ts index f7044e662a..99ce8e7d8e 100644 --- a/examples/openapi-ts-pinia-colada/src/client/types.gen.ts +++ b/examples/openapi-ts-pinia-colada/src/client/types.gen.ts @@ -1,5 +1,9 @@ // This file is auto-generated by @hey-api/openapi-ts +export type ClientOptions = { + baseUrl: 'https://petstore3.swagger.io/api/v3' | (string & {}) +} + export type Order = { complete?: boolean id?: number @@ -687,7 +691,3 @@ export type UpdateUserResponses = { */ 200: unknown } - -export type ClientOptions = { - baseUrl: 'https://petstore3.swagger.io/api/v3' | (string & {}) -} diff --git a/examples/openapi-ts-pinia-colada/src/views/PiniaColadaExample.vue b/examples/openapi-ts-pinia-colada/src/views/PiniaColadaExample.vue index 6930fc78a2..a0e5557da4 100644 --- a/examples/openapi-ts-pinia-colada/src/views/PiniaColadaExample.vue +++ b/examples/openapi-ts-pinia-colada/src/views/PiniaColadaExample.vue @@ -1,11 +1,12 @@ @@ -218,4 +207,6 @@ watch(error, (error) => { + + diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/body-response-text-plain/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/form-data/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts index a8d9f65a68..1d845eca9b 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts @@ -1,16 +1,17 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { _JSONValue, UseMutationOptions, UseQueryOptions } from '@pinia/colada'; +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada'; +import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; import { BarBazService, FooBazService, type Options } from '../sdk.gen'; -import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; +import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooData } from '../types.gen'; export type QueryKey = [ - Pick & { + Pick & { _id: string; baseUrl?: _JSONValue; - headers?: _JSONValue; + body?: _JSONValue; query?: _JSONValue; tags?: _JSONValue; } @@ -21,19 +22,22 @@ const createQueryKey = (id: string, options?: TOptions ] => { const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (tags) { - params.tags = tags as unknown as undefined; + params.tags = tags as unknown as _JSONValue; } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers as unknown as undefined; + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body); + if (normalizedBody !== undefined) { + params.body = normalizedBody; + } } if (options?.path) { params.path = options.path; } - if (options?.query) { - params.query = options.query as unknown as undefined; + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query); + if (normalizedQuery !== undefined) { + params.query = normalizedQuery; + } } return [ params @@ -42,19 +46,17 @@ const createQueryKey = (id: string, options?: TOptions export const getFooQueryKey = (options?: Options) => createQueryKey('getFoo', options); -export const getFooQuery = (options?: Options): UseQueryOptions => { - return { - key: getFooQueryKey(options), - query: async (context) => { - const { data } = await FooBazService.getFoo({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getFooQuery = defineQueryOptions((options?: Options) => ({ + key: getFooQueryKey(options), + query: async (context) => { + const { data } = await FooBazService.getFoo({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const fooPostMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -84,19 +86,17 @@ export const fooPutMutation = (options?: Partial>): UseMutat export const getFooBarQueryKey = (options?: Options) => createQueryKey('getFooBar', options); -export const getFooBarQuery = (options?: Options): UseQueryOptions => { - return { - key: getFooBarQueryKey(options), - query: async (context) => { - const { data } = await BarBazService.getFooBar({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getFooBarQuery = defineQueryOptions((options?: Options) => ({ + key: getFooBarQueryKey(options), + query: async (context) => { + const { data } = await BarBazService.getFooBar({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const fooBarPostMutation = (options?: Partial>): UseMutationOptions, Error> => { return { diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts index d30902d0be..b25e9919a4 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts @@ -1,16 +1,17 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { _JSONValue, UseMutationOptions, UseQueryOptions } from '@pinia/colada'; +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada'; +import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; import { callToTestOrderOfParams, callWithDefaultOptionalParameters, callWithDefaultParameters, callWithDescriptions, callWithDuplicateResponses, callWithNoContentResponse, callWithParameters, callWithResponse, callWithResponseAndNoContentResponse, callWithResponses, callWithResultFromHeader, callWithWeirdParameterNames, collectionFormat, complexTypes, deleteCallWithoutParametersAndResponse, dummyA, dummyB, duplicateName, duplicateName2, duplicateName3, duplicateName4, fooWow, getCallWithoutParametersAndResponse, nonAsciiæøåÆøÅöôêÊ字符串, type Options, patchApiVbyApiVersionNoTag, patchCallWithoutParametersAndResponse, postApiVbyApiVersionBody, postCallWithoutParametersAndResponse, putCallWithoutParametersAndResponse, serviceWithEmptyTag, testErrorCode, types } from '../sdk.gen'; -import type { CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithNoContentResponseData, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, CallWithResponseData, CallWithResponseResponse, CallWithResponsesData, CallWithResponsesError, CallWithResponsesResponse, CallWithResultFromHeaderData, CallWithWeirdParameterNamesData, CollectionFormatData, ComplexTypesData, ComplexTypesResponse, DeleteCallWithoutParametersAndResponseData, DummyAData, DummyBData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, FooWowData, GetCallWithoutParametersAndResponseData, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PatchApiVbyApiVersionNoTagData, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionBodyData, PostApiVbyApiVersionBodyError, PostApiVbyApiVersionBodyResponse, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, ServiceWithEmptyTagData, TestErrorCodeData, TypesData, TypesResponse } from '../types.gen'; +import type { CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithNoContentResponseData, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseData, CallWithResponsesData, CallWithResponsesError, CallWithResponsesResponse, CallWithResultFromHeaderData, CallWithWeirdParameterNamesData, CollectionFormatData, ComplexTypesData, DeleteCallWithoutParametersAndResponseData, DummyAData, DummyBData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, FooWowData, GetCallWithoutParametersAndResponseData, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PatchApiVbyApiVersionNoTagData, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionBodyData, PostApiVbyApiVersionBodyError, PostApiVbyApiVersionBodyResponse, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, ServiceWithEmptyTagData, TestErrorCodeData, TypesData } from '../types.gen'; export type QueryKey = [ - Pick & { + Pick & { _id: string; baseUrl?: _JSONValue; - headers?: _JSONValue; + body?: _JSONValue; query?: _JSONValue; tags?: _JSONValue; } @@ -21,19 +22,22 @@ const createQueryKey = (id: string, options?: TOptions ] => { const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (tags) { - params.tags = tags as unknown as undefined; + params.tags = tags as unknown as _JSONValue; } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers as unknown as undefined; + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body); + if (normalizedBody !== undefined) { + params.body = normalizedBody; + } } if (options?.path) { params.path = options.path; } - if (options?.query) { - params.query = options.query as unknown as undefined; + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query); + if (normalizedQuery !== undefined) { + params.query = normalizedQuery; + } } return [ params @@ -42,19 +46,17 @@ const createQueryKey = (id: string, options?: TOptions export const serviceWithEmptyTagQueryKey = (options?: Options) => createQueryKey('serviceWithEmptyTag', options); -export const serviceWithEmptyTagQuery = (options?: Options): UseQueryOptions => { - return { - key: serviceWithEmptyTagQueryKey(options), - query: async (context) => { - const { data } = await serviceWithEmptyTag({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const serviceWithEmptyTagQuery = defineQueryOptions((options?: Options) => ({ + key: serviceWithEmptyTagQueryKey(options), + query: async (context) => { + const { data } = await serviceWithEmptyTag({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const patchApiVbyApiVersionNoTagMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -97,19 +99,17 @@ export const deleteCallWithoutParametersAndResponseMutation = (options?: Partial export const getCallWithoutParametersAndResponseQueryKey = (options?: Options) => createQueryKey('getCallWithoutParametersAndResponse', options); -export const getCallWithoutParametersAndResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: getCallWithoutParametersAndResponseQueryKey(options), - query: async (context) => { - const { data } = await getCallWithoutParametersAndResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getCallWithoutParametersAndResponseQuery = defineQueryOptions((options?: Options) => ({ + key: getCallWithoutParametersAndResponseQueryKey(options), + query: async (context) => { + const { data } = await getCallWithoutParametersAndResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const patchCallWithoutParametersAndResponseMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -191,19 +191,17 @@ export const callWithWeirdParameterNamesMutation = (options?: Partial) => createQueryKey('callWithDefaultParameters', options); -export const callWithDefaultParametersQuery = (options: Options): UseQueryOptions => { - return { - key: callWithDefaultParametersQueryKey(options), - query: async (context) => { - const { data } = await callWithDefaultParameters({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithDefaultParametersQuery = defineQueryOptions((options: Options) => ({ + key: callWithDefaultParametersQueryKey(options), + query: async (context) => { + const { data } = await callWithDefaultParameters({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithDefaultOptionalParametersMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -246,19 +244,17 @@ export const duplicateNameMutation = (options?: Partial) => createQueryKey('duplicateName2', options); -export const duplicateName2Query = (options?: Options): UseQueryOptions => { - return { - key: duplicateName2QueryKey(options), - query: async (context) => { - const { data } = await duplicateName2({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const duplicateName2Query = defineQueryOptions((options?: Options) => ({ + key: duplicateName2QueryKey(options), + query: async (context) => { + const { data } = await duplicateName2({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const duplicateName3Mutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -288,83 +284,73 @@ export const duplicateName4Mutation = (options?: Partial) => createQueryKey('callWithNoContentResponse', options); -export const callWithNoContentResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithNoContentResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithNoContentResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithNoContentResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithNoContentResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithNoContentResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResponseAndNoContentResponseQueryKey = (options?: Options) => createQueryKey('callWithResponseAndNoContentResponse', options); -export const callWithResponseAndNoContentResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithResponseAndNoContentResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithResponseAndNoContentResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithResponseAndNoContentResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithResponseAndNoContentResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithResponseAndNoContentResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const dummyAQueryKey = (options?: Options) => createQueryKey('dummyA', options); -export const dummyAQuery = (options?: Options): UseQueryOptions => { - return { - key: dummyAQueryKey(options), - query: async (context) => { - const { data } = await dummyA({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const dummyAQuery = defineQueryOptions((options?: Options) => ({ + key: dummyAQueryKey(options), + query: async (context) => { + const { data } = await dummyA({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const dummyBQueryKey = (options?: Options) => createQueryKey('dummyB', options); -export const dummyBQuery = (options?: Options): UseQueryOptions => { - return { - key: dummyBQueryKey(options), - query: async (context) => { - const { data } = await dummyB({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const dummyBQuery = defineQueryOptions((options?: Options) => ({ + key: dummyBQueryKey(options), + query: async (context) => { + const { data } = await dummyB({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResponseQueryKey = (options?: Options) => createQueryKey('callWithResponse', options); -export const callWithResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithDuplicateResponsesMutation = (options?: Partial>): UseMutationOptions, CallWithDuplicateResponsesError> => { return { @@ -394,51 +380,45 @@ export const callWithResponsesMutation = (options?: Partial) => createQueryKey('collectionFormat', options); -export const collectionFormatQuery = (options: Options): UseQueryOptions => { - return { - key: collectionFormatQueryKey(options), - query: async (context) => { - const { data } = await collectionFormat({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const collectionFormatQuery = defineQueryOptions((options: Options) => ({ + key: collectionFormatQueryKey(options), + query: async (context) => { + const { data } = await collectionFormat({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const typesQueryKey = (options: Options) => createQueryKey('types', options); -export const typesQuery = (options: Options): UseQueryOptions => { - return { - key: typesQueryKey(options), - query: async (context) => { - const { data } = await types({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const typesQuery = defineQueryOptions((options: Options) => ({ + key: typesQueryKey(options), + query: async (context) => { + const { data } = await types({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const complexTypesQueryKey = (options: Options) => createQueryKey('complexTypes', options); -export const complexTypesQuery = (options: Options): UseQueryOptions => { - return { - key: complexTypesQueryKey(options), - query: async (context) => { - const { data } = await complexTypes({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const complexTypesQuery = defineQueryOptions((options: Options) => ({ + key: complexTypesQueryKey(options), + query: async (context) => { + const { data } = await complexTypes({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResultFromHeaderMutation = (options?: Partial>): UseMutationOptions, Error> => { return { diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/schema-unknown/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-api-key/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-basic/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/security-oauth2/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-base-path/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers-host/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/servers/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/transforms-read-write/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/body-response-text-plain/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/content-types/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/internal-name-conflict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false-axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/parameter-explode-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts index a8d9f65a68..1d845eca9b 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts @@ -1,16 +1,17 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { _JSONValue, UseMutationOptions, UseQueryOptions } from '@pinia/colada'; +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada'; +import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; import { BarBazService, FooBazService, type Options } from '../sdk.gen'; -import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; +import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooData } from '../types.gen'; export type QueryKey = [ - Pick & { + Pick & { _id: string; baseUrl?: _JSONValue; - headers?: _JSONValue; + body?: _JSONValue; query?: _JSONValue; tags?: _JSONValue; } @@ -21,19 +22,22 @@ const createQueryKey = (id: string, options?: TOptions ] => { const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (tags) { - params.tags = tags as unknown as undefined; + params.tags = tags as unknown as _JSONValue; } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers as unknown as undefined; + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body); + if (normalizedBody !== undefined) { + params.body = normalizedBody; + } } if (options?.path) { params.path = options.path; } - if (options?.query) { - params.query = options.query as unknown as undefined; + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query); + if (normalizedQuery !== undefined) { + params.query = normalizedQuery; + } } return [ params @@ -42,19 +46,17 @@ const createQueryKey = (id: string, options?: TOptions export const getFooQueryKey = (options?: Options) => createQueryKey('getFoo', options); -export const getFooQuery = (options?: Options): UseQueryOptions => { - return { - key: getFooQueryKey(options), - query: async (context) => { - const { data } = await FooBazService.getFoo({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getFooQuery = defineQueryOptions((options?: Options) => ({ + key: getFooQueryKey(options), + query: async (context) => { + const { data } = await FooBazService.getFoo({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const fooPostMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -84,19 +86,17 @@ export const fooPutMutation = (options?: Partial>): UseMutat export const getFooBarQueryKey = (options?: Options) => createQueryKey('getFooBar', options); -export const getFooBarQuery = (options?: Options): UseQueryOptions => { - return { - key: getFooBarQueryKey(options), - query: async (context) => { - const { data } = await BarBazService.getFooBar({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getFooBarQuery = defineQueryOptions((options?: Options) => ({ + key: getFooBarQueryKey(options), + query: async (context) => { + const { data } = await BarBazService.getFooBar({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const fooBarPostMutation = (options?: Partial>): UseMutationOptions, Error> => { return { diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts index 593b8e81ab..5f822ac96f 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts @@ -1,16 +1,17 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { _JSONValue, UseMutationOptions, UseQueryOptions } from '@pinia/colada'; +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada'; +import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; import { apiVVersionODataControllerCount, callToTestOrderOfParams, callWithDefaultOptionalParameters, callWithDefaultParameters, callWithDescriptions, callWithDuplicateResponses, callWithNoContentResponse, callWithParameters, callWithResponse, callWithResponseAndNoContentResponse, callWithResponses, callWithResultFromHeader, callWithWeirdParameterNames, collectionFormat, complexParams, complexTypes, deleteCallWithoutParametersAndResponse, deleteFoo, deprecatedCall, dummyA, dummyB, duplicateName, duplicateName2, duplicateName3, duplicateName4, export_, fileResponse, fooWow, getApiVbyApiVersionSimpleOperation, getCallWithOptionalParam, getCallWithoutParametersAndResponse, import_, multipartRequest, multipartResponse, nonAsciiæøåÆøÅöôêÊ字符串, type Options, patchApiVbyApiVersionNoTag, patchCallWithoutParametersAndResponse, postApiVbyApiVersionFormData, postApiVbyApiVersionRequestBody, postCallWithOptionalParam, postCallWithoutParametersAndResponse, putCallWithoutParametersAndResponse, putWithFormUrlEncoded, testErrorCode, types, uploadFile } from '../sdk.gen'; -import type { ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponse, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithNoContentResponseData, CallWithNoContentResponseResponse, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, CallWithResponseData, CallWithResponseResponse, CallWithResponsesData, CallWithResponsesError, CallWithResponsesResponse, CallWithResultFromHeaderData, CallWithWeirdParameterNamesData, CollectionFormatData, ComplexParamsData, ComplexParamsResponse, ComplexTypesData, ComplexTypesResponse, DeleteCallWithoutParametersAndResponseData, DeleteFooData3, DeprecatedCallData, DummyAData, DummyAResponse, DummyBData, DummyBResponse, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, ExportData, FileResponseData, FileResponseResponse, FooWowData, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationError, GetApiVbyApiVersionSimpleOperationResponse, GetCallWithOptionalParamData, GetCallWithoutParametersAndResponseData, ImportData, ImportResponse, MultipartRequestData, MultipartResponseData, MultipartResponseResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PatchApiVbyApiVersionNoTagData, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, PutWithFormUrlEncodedData, TestErrorCodeData, TypesData, TypesResponse, UploadFileData, UploadFileResponse } from '../types.gen'; +import type { ApiVVersionODataControllerCountData, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithNoContentResponseData, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseData, CallWithResponsesData, CallWithResponsesError, CallWithResponsesResponse, CallWithResultFromHeaderData, CallWithWeirdParameterNamesData, CollectionFormatData, ComplexParamsData, ComplexParamsResponse, ComplexTypesData, DeleteCallWithoutParametersAndResponseData, DeleteFooData3, DeprecatedCallData, DummyAData, DummyBData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, ExportData, FileResponseData, FooWowData, GetApiVbyApiVersionSimpleOperationData, GetCallWithOptionalParamData, GetCallWithoutParametersAndResponseData, ImportData, ImportResponse, MultipartRequestData, MultipartResponseData, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PatchApiVbyApiVersionNoTagData, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, PutWithFormUrlEncodedData, TestErrorCodeData, TypesData, UploadFileData, UploadFileResponse } from '../types.gen'; export type QueryKey = [ - Pick & { + Pick & { _id: string; baseUrl?: _JSONValue; - headers?: _JSONValue; + body?: _JSONValue; query?: _JSONValue; tags?: _JSONValue; } @@ -21,19 +22,22 @@ const createQueryKey = (id: string, options?: TOptions ] => { const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (tags) { - params.tags = tags as unknown as undefined; + params.tags = tags as unknown as _JSONValue; } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers as unknown as undefined; + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body); + if (normalizedBody !== undefined) { + params.body = normalizedBody; + } } if (options?.path) { params.path = options.path; } - if (options?.query) { - params.query = options.query as unknown as undefined; + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query); + if (normalizedQuery !== undefined) { + params.query = normalizedQuery; + } } return [ params @@ -42,19 +46,17 @@ const createQueryKey = (id: string, options?: TOptions export const exportQueryKey = (options?: Options) => createQueryKey('export', options); -export const exportQuery = (options?: Options): UseQueryOptions => { - return { - key: exportQueryKey(options), - query: async (context) => { - const { data } = await export_({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const exportQuery = defineQueryOptions((options?: Options) => ({ + key: exportQueryKey(options), + query: async (context) => { + const { data } = await export_({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const patchApiVbyApiVersionNoTagMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -97,35 +99,31 @@ export const fooWowMutation = (options?: Partial>): UseMutat export const apiVVersionODataControllerCountQueryKey = (options?: Options) => createQueryKey('apiVVersionODataControllerCount', options); -export const apiVVersionODataControllerCountQuery = (options?: Options): UseQueryOptions => { - return { - key: apiVVersionODataControllerCountQueryKey(options), - query: async (context) => { - const { data } = await apiVVersionODataControllerCount({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const apiVVersionODataControllerCountQuery = defineQueryOptions((options?: Options) => ({ + key: apiVVersionODataControllerCountQueryKey(options), + query: async (context) => { + const { data } = await apiVVersionODataControllerCount({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const getApiVbyApiVersionSimpleOperationQueryKey = (options: Options) => createQueryKey('getApiVbyApiVersionSimpleOperation', options); -export const getApiVbyApiVersionSimpleOperationQuery = (options: Options): UseQueryOptions => { - return { - key: getApiVbyApiVersionSimpleOperationQueryKey(options), - query: async (context) => { - const { data } = await getApiVbyApiVersionSimpleOperation({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getApiVbyApiVersionSimpleOperationQuery = defineQueryOptions((options: Options) => ({ + key: getApiVbyApiVersionSimpleOperationQueryKey(options), + query: async (context) => { + const { data } = await getApiVbyApiVersionSimpleOperation({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const deleteCallWithoutParametersAndResponseMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -142,19 +140,17 @@ export const deleteCallWithoutParametersAndResponseMutation = (options?: Partial export const getCallWithoutParametersAndResponseQueryKey = (options?: Options) => createQueryKey('getCallWithoutParametersAndResponse', options); -export const getCallWithoutParametersAndResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: getCallWithoutParametersAndResponseQueryKey(options), - query: async (context) => { - const { data } = await getCallWithoutParametersAndResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getCallWithoutParametersAndResponseQuery = defineQueryOptions((options?: Options) => ({ + key: getCallWithoutParametersAndResponseQueryKey(options), + query: async (context) => { + const { data } = await getCallWithoutParametersAndResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const patchCallWithoutParametersAndResponseMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -265,19 +261,17 @@ export const callWithWeirdParameterNamesMutation = (options?: Partial) => createQueryKey('getCallWithOptionalParam', options); -export const getCallWithOptionalParamQuery = (options: Options): UseQueryOptions => { - return { - key: getCallWithOptionalParamQueryKey(options), - query: async (context) => { - const { data } = await getCallWithOptionalParam({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getCallWithOptionalParamQuery = defineQueryOptions((options: Options) => ({ + key: getCallWithOptionalParamQueryKey(options), + query: async (context) => { + const { data } = await getCallWithOptionalParam({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const postCallWithOptionalParamMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -320,19 +314,17 @@ export const postApiVbyApiVersionFormDataMutation = (options?: Partial) => createQueryKey('callWithDefaultParameters', options); -export const callWithDefaultParametersQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithDefaultParametersQueryKey(options), - query: async (context) => { - const { data } = await callWithDefaultParameters({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithDefaultParametersQuery = defineQueryOptions((options?: Options) => ({ + key: callWithDefaultParametersQueryKey(options), + query: async (context) => { + const { data } = await callWithDefaultParameters({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithDefaultOptionalParametersMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -375,19 +367,17 @@ export const duplicateNameMutation = (options?: Partial) => createQueryKey('duplicateName2', options); -export const duplicateName2Query = (options?: Options): UseQueryOptions => { - return { - key: duplicateName2QueryKey(options), - query: async (context) => { - const { data } = await duplicateName2({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const duplicateName2Query = defineQueryOptions((options?: Options) => ({ + key: duplicateName2QueryKey(options), + query: async (context) => { + const { data } = await duplicateName2({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const duplicateName3Mutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -417,83 +407,73 @@ export const duplicateName4Mutation = (options?: Partial) => createQueryKey('callWithNoContentResponse', options); -export const callWithNoContentResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithNoContentResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithNoContentResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithNoContentResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithNoContentResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithNoContentResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResponseAndNoContentResponseQueryKey = (options?: Options) => createQueryKey('callWithResponseAndNoContentResponse', options); -export const callWithResponseAndNoContentResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithResponseAndNoContentResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithResponseAndNoContentResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithResponseAndNoContentResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithResponseAndNoContentResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithResponseAndNoContentResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const dummyAQueryKey = (options?: Options) => createQueryKey('dummyA', options); -export const dummyAQuery = (options?: Options): UseQueryOptions => { - return { - key: dummyAQueryKey(options), - query: async (context) => { - const { data } = await dummyA({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const dummyAQuery = defineQueryOptions((options?: Options) => ({ + key: dummyAQueryKey(options), + query: async (context) => { + const { data } = await dummyA({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const dummyBQueryKey = (options?: Options) => createQueryKey('dummyB', options); -export const dummyBQuery = (options?: Options): UseQueryOptions => { - return { - key: dummyBQueryKey(options), - query: async (context) => { - const { data } = await dummyB({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const dummyBQuery = defineQueryOptions((options?: Options) => ({ + key: dummyBQueryKey(options), + query: async (context) => { + const { data } = await dummyB({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResponseQueryKey = (options?: Options) => createQueryKey('callWithResponse', options); -export const callWithResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithDuplicateResponsesMutation = (options?: Partial>): UseMutationOptions, CallWithDuplicateResponsesError> => { return { @@ -523,35 +503,31 @@ export const callWithResponsesMutation = (options?: Partial) => createQueryKey('collectionFormat', options); -export const collectionFormatQuery = (options: Options): UseQueryOptions => { - return { - key: collectionFormatQueryKey(options), - query: async (context) => { - const { data } = await collectionFormat({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const collectionFormatQuery = defineQueryOptions((options: Options) => ({ + key: collectionFormatQueryKey(options), + query: async (context) => { + const { data } = await collectionFormat({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const typesQueryKey = (options: Options) => createQueryKey('types', options); -export const typesQuery = (options: Options): UseQueryOptions => { - return { - key: typesQueryKey(options), - query: async (context) => { - const { data } = await types({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const typesQuery = defineQueryOptions((options: Options) => ({ + key: typesQueryKey(options), + query: async (context) => { + const { data } = await types({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const uploadFileMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -568,51 +544,45 @@ export const uploadFileMutation = (options?: Partial>): export const fileResponseQueryKey = (options: Options) => createQueryKey('fileResponse', options); -export const fileResponseQuery = (options: Options): UseQueryOptions => { - return { - key: fileResponseQueryKey(options), - query: async (context) => { - const { data } = await fileResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const fileResponseQuery = defineQueryOptions((options: Options) => ({ + key: fileResponseQueryKey(options), + query: async (context) => { + const { data } = await fileResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const complexTypesQueryKey = (options: Options) => createQueryKey('complexTypes', options); -export const complexTypesQuery = (options: Options): UseQueryOptions => { - return { - key: complexTypesQueryKey(options), - query: async (context) => { - const { data } = await complexTypes({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const complexTypesQuery = defineQueryOptions((options: Options) => ({ + key: complexTypesQueryKey(options), + query: async (context) => { + const { data } = await complexTypes({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const multipartResponseQueryKey = (options?: Options) => createQueryKey('multipartResponse', options); -export const multipartResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: multipartResponseQueryKey(options), - query: async (context) => { - const { data } = await multipartResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const multipartResponseQuery = defineQueryOptions((options?: Options) => ({ + key: multipartResponseQueryKey(options), + query: async (context) => { + const { data } = await multipartResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const multipartRequestMutation = (options?: Partial>): UseMutationOptions, Error> => { return { diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-api-key/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-http-bearer/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-oauth2/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/security-open-id-connect/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/servers/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-all-of/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-any-of-null/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transformers-array/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-read-write/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/body-response-text-plain/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-number/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-strict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/base-url-string/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/clean-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-optional/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/sdk-client-required/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/index.ts index 771ff86c9e..f32aaa771d 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen.js'; export { buildClientParams } from '../core/params.gen.js'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js'; export { createClient } from './client.gen.js'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-axios/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-number/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-strict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/base-url-string/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/clean-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-optional/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/sdk-client-required/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/index.ts index 99df3b8511..b4e8e87d0f 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen.js'; export { buildClientParams } from '../core/params.gen.js'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js'; export { createClient } from './client.gen.js'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-fetch/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-number/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-strict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/base-url-string/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/clean-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-optional/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/sdk-client-required/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/index.ts index 771ff86c9e..f32aaa771d 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen.js'; export { buildClientParams } from '../core/params.gen.js'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js'; export { createClient } from './client.gen.js'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-next/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-number/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-strict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/base-url-string/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/clean-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-optional/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/sdk-client-required/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/index.ts index 8bfc39df07..cbc00f0f6c 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen.js'; export { buildClientParams } from '../core/params.gen.js'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js'; export { createClient } from './client.gen.js'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-nuxt/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-number/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-strict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/base-url-string/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/clean-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-optional/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/sdk-client-required/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/client/index.ts index 99df3b8511..b4e8e87d0f 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen.js'; export { buildClientParams } from '../core/params.gen.js'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js'; export { createClient } from './client.gen.js'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/clients/@hey-api/client-ofetch/tsconfig-nodenext-sdk/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/content-types/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/headers/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/internal-name-conflict/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/pagination-ref/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false-axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/parameter-explode-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default-class/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@angular/common/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes-instance/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/client-fetch/sdk-nested-classes/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/default/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/instance/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/sdk/throwOnError/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-valibot/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/transformers/type-format-zod/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-custom-name/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@hey-api/typescript/transforms-read-write-ignore/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts index a8d9f65a68..1d845eca9b 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/@pinia/colada.gen.ts @@ -1,16 +1,17 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { _JSONValue, UseMutationOptions, UseQueryOptions } from '@pinia/colada'; +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada'; +import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; import { BarBazService, FooBazService, type Options } from '../sdk.gen'; -import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooBarResponse, GetFooData, GetFooResponse } from '../types.gen'; +import type { FooBarPostData, FooBarPostResponse, FooBarPutData, FooBarPutResponse, FooPostData, FooPostResponse, FooPutData, FooPutResponse, GetFooBarData, GetFooData } from '../types.gen'; export type QueryKey = [ - Pick & { + Pick & { _id: string; baseUrl?: _JSONValue; - headers?: _JSONValue; + body?: _JSONValue; query?: _JSONValue; tags?: _JSONValue; } @@ -21,19 +22,22 @@ const createQueryKey = (id: string, options?: TOptions ] => { const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (tags) { - params.tags = tags as unknown as undefined; + params.tags = tags as unknown as _JSONValue; } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers as unknown as undefined; + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body); + if (normalizedBody !== undefined) { + params.body = normalizedBody; + } } if (options?.path) { params.path = options.path; } - if (options?.query) { - params.query = options.query as unknown as undefined; + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query); + if (normalizedQuery !== undefined) { + params.query = normalizedQuery; + } } return [ params @@ -42,19 +46,17 @@ const createQueryKey = (id: string, options?: TOptions export const getFooQueryKey = (options?: Options) => createQueryKey('getFoo', options); -export const getFooQuery = (options?: Options): UseQueryOptions => { - return { - key: getFooQueryKey(options), - query: async (context) => { - const { data } = await FooBazService.getFoo({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getFooQuery = defineQueryOptions((options?: Options) => ({ + key: getFooQueryKey(options), + query: async (context) => { + const { data } = await FooBazService.getFoo({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const fooPostMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -84,19 +86,17 @@ export const fooPutMutation = (options?: Partial>): UseMutat export const getFooBarQueryKey = (options?: Options) => createQueryKey('getFooBar', options); -export const getFooBarQuery = (options?: Options): UseQueryOptions => { - return { - key: getFooBarQueryKey(options), - query: async (context) => { - const { data } = await BarBazService.getFooBar({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getFooBarQuery = defineQueryOptions((options?: Options) => ({ + key: getFooBarQueryKey(options), + query: async (context) => { + const { data } = await BarBazService.getFooBar({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const fooBarPostMutation = (options?: Partial>): UseMutationOptions, Error> => { return { diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts index 593b8e81ab..5f822ac96f 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/@pinia/colada.gen.ts @@ -1,16 +1,17 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { _JSONValue, UseMutationOptions, UseQueryOptions } from '@pinia/colada'; +import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@pinia/colada'; +import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; import { apiVVersionODataControllerCount, callToTestOrderOfParams, callWithDefaultOptionalParameters, callWithDefaultParameters, callWithDescriptions, callWithDuplicateResponses, callWithNoContentResponse, callWithParameters, callWithResponse, callWithResponseAndNoContentResponse, callWithResponses, callWithResultFromHeader, callWithWeirdParameterNames, collectionFormat, complexParams, complexTypes, deleteCallWithoutParametersAndResponse, deleteFoo, deprecatedCall, dummyA, dummyB, duplicateName, duplicateName2, duplicateName3, duplicateName4, export_, fileResponse, fooWow, getApiVbyApiVersionSimpleOperation, getCallWithOptionalParam, getCallWithoutParametersAndResponse, import_, multipartRequest, multipartResponse, nonAsciiæøåÆøÅöôêÊ字符串, type Options, patchApiVbyApiVersionNoTag, patchCallWithoutParametersAndResponse, postApiVbyApiVersionFormData, postApiVbyApiVersionRequestBody, postCallWithOptionalParam, postCallWithoutParametersAndResponse, putCallWithoutParametersAndResponse, putWithFormUrlEncoded, testErrorCode, types, uploadFile } from '../sdk.gen'; -import type { ApiVVersionODataControllerCountData, ApiVVersionODataControllerCountResponse, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithNoContentResponseData, CallWithNoContentResponseResponse, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseAndNoContentResponseResponse, CallWithResponseData, CallWithResponseResponse, CallWithResponsesData, CallWithResponsesError, CallWithResponsesResponse, CallWithResultFromHeaderData, CallWithWeirdParameterNamesData, CollectionFormatData, ComplexParamsData, ComplexParamsResponse, ComplexTypesData, ComplexTypesResponse, DeleteCallWithoutParametersAndResponseData, DeleteFooData3, DeprecatedCallData, DummyAData, DummyAResponse, DummyBData, DummyBResponse, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, ExportData, FileResponseData, FileResponseResponse, FooWowData, GetApiVbyApiVersionSimpleOperationData, GetApiVbyApiVersionSimpleOperationError, GetApiVbyApiVersionSimpleOperationResponse, GetCallWithOptionalParamData, GetCallWithoutParametersAndResponseData, ImportData, ImportResponse, MultipartRequestData, MultipartResponseData, MultipartResponseResponse, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PatchApiVbyApiVersionNoTagData, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, PutWithFormUrlEncodedData, TestErrorCodeData, TypesData, TypesResponse, UploadFileData, UploadFileResponse } from '../types.gen'; +import type { ApiVVersionODataControllerCountData, CallToTestOrderOfParamsData, CallWithDefaultOptionalParametersData, CallWithDefaultParametersData, CallWithDescriptionsData, CallWithDuplicateResponsesData, CallWithDuplicateResponsesError, CallWithDuplicateResponsesResponse, CallWithNoContentResponseData, CallWithParametersData, CallWithResponseAndNoContentResponseData, CallWithResponseData, CallWithResponsesData, CallWithResponsesError, CallWithResponsesResponse, CallWithResultFromHeaderData, CallWithWeirdParameterNamesData, CollectionFormatData, ComplexParamsData, ComplexParamsResponse, ComplexTypesData, DeleteCallWithoutParametersAndResponseData, DeleteFooData3, DeprecatedCallData, DummyAData, DummyBData, DuplicateName2Data, DuplicateName3Data, DuplicateName4Data, DuplicateNameData, ExportData, FileResponseData, FooWowData, GetApiVbyApiVersionSimpleOperationData, GetCallWithOptionalParamData, GetCallWithoutParametersAndResponseData, ImportData, ImportResponse, MultipartRequestData, MultipartResponseData, NonAsciiæøåÆøÅöôêÊ字符串Data, NonAsciiæøåÆøÅöôêÊ字符串Response, PatchApiVbyApiVersionNoTagData, PatchCallWithoutParametersAndResponseData, PostApiVbyApiVersionFormDataData, PostApiVbyApiVersionRequestBodyData, PostCallWithOptionalParamData, PostCallWithOptionalParamResponse, PostCallWithoutParametersAndResponseData, PutCallWithoutParametersAndResponseData, PutWithFormUrlEncodedData, TestErrorCodeData, TypesData, UploadFileData, UploadFileResponse } from '../types.gen'; export type QueryKey = [ - Pick & { + Pick & { _id: string; baseUrl?: _JSONValue; - headers?: _JSONValue; + body?: _JSONValue; query?: _JSONValue; tags?: _JSONValue; } @@ -21,19 +22,22 @@ const createQueryKey = (id: string, options?: TOptions ] => { const params: QueryKey[0] = { _id: id, baseUrl: options?.baseUrl || (options?.client ?? client).getConfig().baseUrl } as QueryKey[0]; if (tags) { - params.tags = tags as unknown as undefined; + params.tags = tags as unknown as _JSONValue; } - if (options?.body) { - params.body = options.body; - } - if (options?.headers) { - params.headers = options.headers as unknown as undefined; + if (options?.body !== undefined) { + const normalizedBody = serializeQueryKeyValue(options.body); + if (normalizedBody !== undefined) { + params.body = normalizedBody; + } } if (options?.path) { params.path = options.path; } - if (options?.query) { - params.query = options.query as unknown as undefined; + if (options?.query !== undefined) { + const normalizedQuery = serializeQueryKeyValue(options.query); + if (normalizedQuery !== undefined) { + params.query = normalizedQuery; + } } return [ params @@ -42,19 +46,17 @@ const createQueryKey = (id: string, options?: TOptions export const exportQueryKey = (options?: Options) => createQueryKey('export', options); -export const exportQuery = (options?: Options): UseQueryOptions => { - return { - key: exportQueryKey(options), - query: async (context) => { - const { data } = await export_({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const exportQuery = defineQueryOptions((options?: Options) => ({ + key: exportQueryKey(options), + query: async (context) => { + const { data } = await export_({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const patchApiVbyApiVersionNoTagMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -97,35 +99,31 @@ export const fooWowMutation = (options?: Partial>): UseMutat export const apiVVersionODataControllerCountQueryKey = (options?: Options) => createQueryKey('apiVVersionODataControllerCount', options); -export const apiVVersionODataControllerCountQuery = (options?: Options): UseQueryOptions => { - return { - key: apiVVersionODataControllerCountQueryKey(options), - query: async (context) => { - const { data } = await apiVVersionODataControllerCount({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const apiVVersionODataControllerCountQuery = defineQueryOptions((options?: Options) => ({ + key: apiVVersionODataControllerCountQueryKey(options), + query: async (context) => { + const { data } = await apiVVersionODataControllerCount({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const getApiVbyApiVersionSimpleOperationQueryKey = (options: Options) => createQueryKey('getApiVbyApiVersionSimpleOperation', options); -export const getApiVbyApiVersionSimpleOperationQuery = (options: Options): UseQueryOptions => { - return { - key: getApiVbyApiVersionSimpleOperationQueryKey(options), - query: async (context) => { - const { data } = await getApiVbyApiVersionSimpleOperation({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getApiVbyApiVersionSimpleOperationQuery = defineQueryOptions((options: Options) => ({ + key: getApiVbyApiVersionSimpleOperationQueryKey(options), + query: async (context) => { + const { data } = await getApiVbyApiVersionSimpleOperation({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const deleteCallWithoutParametersAndResponseMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -142,19 +140,17 @@ export const deleteCallWithoutParametersAndResponseMutation = (options?: Partial export const getCallWithoutParametersAndResponseQueryKey = (options?: Options) => createQueryKey('getCallWithoutParametersAndResponse', options); -export const getCallWithoutParametersAndResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: getCallWithoutParametersAndResponseQueryKey(options), - query: async (context) => { - const { data } = await getCallWithoutParametersAndResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getCallWithoutParametersAndResponseQuery = defineQueryOptions((options?: Options) => ({ + key: getCallWithoutParametersAndResponseQueryKey(options), + query: async (context) => { + const { data } = await getCallWithoutParametersAndResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const patchCallWithoutParametersAndResponseMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -265,19 +261,17 @@ export const callWithWeirdParameterNamesMutation = (options?: Partial) => createQueryKey('getCallWithOptionalParam', options); -export const getCallWithOptionalParamQuery = (options: Options): UseQueryOptions => { - return { - key: getCallWithOptionalParamQueryKey(options), - query: async (context) => { - const { data } = await getCallWithOptionalParam({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const getCallWithOptionalParamQuery = defineQueryOptions((options: Options) => ({ + key: getCallWithOptionalParamQueryKey(options), + query: async (context) => { + const { data } = await getCallWithOptionalParam({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const postCallWithOptionalParamMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -320,19 +314,17 @@ export const postApiVbyApiVersionFormDataMutation = (options?: Partial) => createQueryKey('callWithDefaultParameters', options); -export const callWithDefaultParametersQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithDefaultParametersQueryKey(options), - query: async (context) => { - const { data } = await callWithDefaultParameters({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithDefaultParametersQuery = defineQueryOptions((options?: Options) => ({ + key: callWithDefaultParametersQueryKey(options), + query: async (context) => { + const { data } = await callWithDefaultParameters({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithDefaultOptionalParametersMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -375,19 +367,17 @@ export const duplicateNameMutation = (options?: Partial) => createQueryKey('duplicateName2', options); -export const duplicateName2Query = (options?: Options): UseQueryOptions => { - return { - key: duplicateName2QueryKey(options), - query: async (context) => { - const { data } = await duplicateName2({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const duplicateName2Query = defineQueryOptions((options?: Options) => ({ + key: duplicateName2QueryKey(options), + query: async (context) => { + const { data } = await duplicateName2({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const duplicateName3Mutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -417,83 +407,73 @@ export const duplicateName4Mutation = (options?: Partial) => createQueryKey('callWithNoContentResponse', options); -export const callWithNoContentResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithNoContentResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithNoContentResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithNoContentResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithNoContentResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithNoContentResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResponseAndNoContentResponseQueryKey = (options?: Options) => createQueryKey('callWithResponseAndNoContentResponse', options); -export const callWithResponseAndNoContentResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithResponseAndNoContentResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithResponseAndNoContentResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithResponseAndNoContentResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithResponseAndNoContentResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithResponseAndNoContentResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const dummyAQueryKey = (options?: Options) => createQueryKey('dummyA', options); -export const dummyAQuery = (options?: Options): UseQueryOptions => { - return { - key: dummyAQueryKey(options), - query: async (context) => { - const { data } = await dummyA({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const dummyAQuery = defineQueryOptions((options?: Options) => ({ + key: dummyAQueryKey(options), + query: async (context) => { + const { data } = await dummyA({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const dummyBQueryKey = (options?: Options) => createQueryKey('dummyB', options); -export const dummyBQuery = (options?: Options): UseQueryOptions => { - return { - key: dummyBQueryKey(options), - query: async (context) => { - const { data } = await dummyB({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const dummyBQuery = defineQueryOptions((options?: Options) => ({ + key: dummyBQueryKey(options), + query: async (context) => { + const { data } = await dummyB({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithResponseQueryKey = (options?: Options) => createQueryKey('callWithResponse', options); -export const callWithResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: callWithResponseQueryKey(options), - query: async (context) => { - const { data } = await callWithResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const callWithResponseQuery = defineQueryOptions((options?: Options) => ({ + key: callWithResponseQueryKey(options), + query: async (context) => { + const { data } = await callWithResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const callWithDuplicateResponsesMutation = (options?: Partial>): UseMutationOptions, CallWithDuplicateResponsesError> => { return { @@ -523,35 +503,31 @@ export const callWithResponsesMutation = (options?: Partial) => createQueryKey('collectionFormat', options); -export const collectionFormatQuery = (options: Options): UseQueryOptions => { - return { - key: collectionFormatQueryKey(options), - query: async (context) => { - const { data } = await collectionFormat({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const collectionFormatQuery = defineQueryOptions((options: Options) => ({ + key: collectionFormatQueryKey(options), + query: async (context) => { + const { data } = await collectionFormat({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const typesQueryKey = (options: Options) => createQueryKey('types', options); -export const typesQuery = (options: Options): UseQueryOptions => { - return { - key: typesQueryKey(options), - query: async (context) => { - const { data } = await types({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const typesQuery = defineQueryOptions((options: Options) => ({ + key: typesQueryKey(options), + query: async (context) => { + const { data } = await types({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const uploadFileMutation = (options?: Partial>): UseMutationOptions, Error> => { return { @@ -568,51 +544,45 @@ export const uploadFileMutation = (options?: Partial>): export const fileResponseQueryKey = (options: Options) => createQueryKey('fileResponse', options); -export const fileResponseQuery = (options: Options): UseQueryOptions => { - return { - key: fileResponseQueryKey(options), - query: async (context) => { - const { data } = await fileResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const fileResponseQuery = defineQueryOptions((options: Options) => ({ + key: fileResponseQueryKey(options), + query: async (context) => { + const { data } = await fileResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const complexTypesQueryKey = (options: Options) => createQueryKey('complexTypes', options); -export const complexTypesQuery = (options: Options): UseQueryOptions => { - return { - key: complexTypesQueryKey(options), - query: async (context) => { - const { data } = await complexTypes({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const complexTypesQuery = defineQueryOptions((options: Options) => ({ + key: complexTypesQueryKey(options), + query: async (context) => { + const { data } = await complexTypes({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const multipartResponseQueryKey = (options?: Options) => createQueryKey('multipartResponse', options); -export const multipartResponseQuery = (options?: Options): UseQueryOptions => { - return { - key: multipartResponseQueryKey(options), - query: async (context) => { - const { data } = await multipartResponse({ - ...options, - ...context, - throwOnError: true - }); - return data; - } - }; -}; +export const multipartResponseQuery = defineQueryOptions((options?: Options) => ({ + key: multipartResponseQueryKey(options), + query: async (context) => { + const { data } = await multipartResponse({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); export const multipartRequestMutation = (options?: Partial>): UseMutationOptions, Error> => { return { diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@pinia/colada/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/angular-query-experimental/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/react-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/solid-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/svelte-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/asClass/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/@tanstack/vue-query/name-builder/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-api-key/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-false/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-http-bearer/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-oauth2/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/security-open-id-connect/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/servers/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-angular/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-axios/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-fetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/client/index.ts index 8ddc04f425..cff1d39c90 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-next/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/client/index.ts index a2e8f2493f..f442748d17 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-nuxt/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/sse-ofetch/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-all-of/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-any-of-null/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transformers-array/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/transforms-read-write/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/client/index.ts b/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/client/index.ts index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/client/index.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/client/index.ts @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/core/queryKeySerializer.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/plugins/@tanstack/meta/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/client/index.ts.snap b/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/client/index.ts.snap index 318a84b6a8..cbf8dfeedb 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/client/index.ts.snap +++ b/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/client/index.ts.snap @@ -8,6 +8,7 @@ export { urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen'; export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; export { createClient } from './client.gen'; export type { Client, diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/core/queryKeySerializer.gen.ts.snap b/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/core/queryKeySerializer.gen.ts.snap new file mode 100644 index 0000000000..d3bb68396e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/test/generated/v3_no_index/core/queryKeySerializer.gen.ts.snap @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts-tests/main/test/tsconfig/tsconfig.nodenext.json b/packages/openapi-ts-tests/main/test/tsconfig/tsconfig.nodenext.json index 894f38263d..090bb97ca5 100644 --- a/packages/openapi-ts-tests/main/test/tsconfig/tsconfig.nodenext.json +++ b/packages/openapi-ts-tests/main/test/tsconfig/tsconfig.nodenext.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "compilerOptions": { "declaration": false, "moduleResolution": "nodenext", diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-angular/bundle/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-angular/bundle/index.ts index a237c773b0..c24fdc3b18 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-angular/bundle/index.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-angular/bundle/index.ts @@ -6,6 +6,7 @@ export { urlSearchParamsBodySerializer, } from '../../client-core/bundle/bodySerializer'; export { buildClientParams } from '../../client-core/bundle/params'; +export { serializeQueryKeyValue } from '../../client-core/bundle/queryKeySerializer'; export { createClient } from './client'; export type { Client, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-axios/bundle/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-axios/bundle/index.ts index 1d5cdeebc4..39350a4f1a 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-axios/bundle/index.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-axios/bundle/index.ts @@ -6,6 +6,7 @@ export { urlSearchParamsBodySerializer, } from '../../client-core/bundle/bodySerializer'; export { buildClientParams } from '../../client-core/bundle/params'; +export { serializeQueryKeyValue } from '../../client-core/bundle/queryKeySerializer'; export { createClient } from './client'; export type { Client, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/__tests__/queryKeySerializer.test.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/__tests__/queryKeySerializer.test.ts new file mode 100644 index 0000000000..d3e8c5f6aa --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/__tests__/queryKeySerializer.test.ts @@ -0,0 +1,111 @@ +import { describe, expect, it } from 'vitest'; + +import { + queryKeyJsonReplacer, + serializeQueryKeyValue, + stringifyToJsonValue, +} from '../bundle/queryKeySerializer'; + +describe('query key helpers', () => { + describe('queryKeyJsonReplacer', () => { + it('converts bigint to string', () => { + expect(queryKeyJsonReplacer('value', 1n)).toBe('1'); + }); + + it('converts Date to ISO string', () => { + const date = new Date('2025-01-01T12:34:56.000Z'); + expect(queryKeyJsonReplacer('value', date)).toBe(date.toISOString()); + }); + + it('drops unsupported values', () => { + expect(queryKeyJsonReplacer('value', undefined)).toBeUndefined(); + expect(queryKeyJsonReplacer('value', () => {})).toBeUndefined(); + expect(queryKeyJsonReplacer('value', Symbol('s'))).toBeUndefined(); + }); + }); + + describe('stringifyToJsonValue', () => { + it('produces JSON-safe structures', () => { + const input = { + a: 1n, + b: new Date('2025-01-02T00:00:00.000Z'), + c: () => {}, + d: undefined, + }; + + expect(stringifyToJsonValue(input)).toEqual({ + a: '1', + b: '2025-01-02T00:00:00.000Z', + }); + }); + + it('returns undefined when value cannot be stringified', () => { + const circular: { self?: unknown } = {}; + circular.self = circular; + + expect(stringifyToJsonValue(circular)).toBeUndefined(); + }); + }); + + describe('serializeQueryKeyValue', () => { + it('handles primitives and null', () => { + expect(serializeQueryKeyValue(null)).toBeNull(); + expect(serializeQueryKeyValue('')).toBe(''); + expect(serializeQueryKeyValue(0)).toBe(0); + expect(serializeQueryKeyValue(false)).toBe(false); + }); + + it('converts special primitives', () => { + expect(serializeQueryKeyValue(1n)).toBe('1'); + + const date = new Date('2025-03-04T05:06:07.000Z'); + expect(serializeQueryKeyValue(date)).toBe(date.toISOString()); + }); + + it('normalizes arrays', () => { + const date = new Date('2025-03-04T05:06:07.000Z'); + expect( + serializeQueryKeyValue([1n, date, undefined, () => {}, 'ok']), + ).toEqual(['1', date.toISOString(), null, null, 'ok']); + }); + + it('normalizes plain objects', () => { + const result = serializeQueryKeyValue({ + a: 1, + b: 1n, + c: new Date('2025-06-07T08:09:10.000Z'), + d: undefined, + e: () => {}, + }); + + expect(result).toEqual({ + a: 1, + b: '1', + c: '2025-06-07T08:09:10.000Z', + }); + }); + + it('supports objects with null prototype', () => { + const value = Object.assign(Object.create(null), { a: 1 }); + expect(serializeQueryKeyValue(value)).toEqual({ a: 1 }); + }); + + it('serializes URLSearchParams as JSON object', () => { + const params = new URLSearchParams(); + params.append('a', '1'); + params.append('a', '2'); + params.append('b', 'foo'); + + expect(serializeQueryKeyValue(params)).toEqual({ + a: ['1', '2'], + b: 'foo', + }); + }); + + it('rejects unsupported structures', () => { + expect(serializeQueryKeyValue(new Map())).toBeUndefined(); + expect(serializeQueryKeyValue(new Set())).toBeUndefined(); + expect(serializeQueryKeyValue(new (class Example {})())).toBeUndefined(); + }); + }); +}); diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-core/bundle/queryKeySerializer.ts b/packages/openapi-ts/src/plugins/@hey-api/client-core/bundle/queryKeySerializer.ts new file mode 100644 index 0000000000..b708f22c96 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@hey-api/client-core/bundle/queryKeySerializer.ts @@ -0,0 +1,134 @@ +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/bundle/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/bundle/index.ts index a237c773b0..c24fdc3b18 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-fetch/bundle/index.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-fetch/bundle/index.ts @@ -6,6 +6,7 @@ export { urlSearchParamsBodySerializer, } from '../../client-core/bundle/bodySerializer'; export { buildClientParams } from '../../client-core/bundle/params'; +export { serializeQueryKeyValue } from '../../client-core/bundle/queryKeySerializer'; export { createClient } from './client'; export type { Client, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-next/bundle/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-next/bundle/index.ts index 1d5cdeebc4..39350a4f1a 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-next/bundle/index.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-next/bundle/index.ts @@ -6,6 +6,7 @@ export { urlSearchParamsBodySerializer, } from '../../client-core/bundle/bodySerializer'; export { buildClientParams } from '../../client-core/bundle/params'; +export { serializeQueryKeyValue } from '../../client-core/bundle/queryKeySerializer'; export { createClient } from './client'; export type { Client, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/bundle/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/bundle/index.ts index 2c17fa8288..cc8c9d8166 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/bundle/index.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-nuxt/bundle/index.ts @@ -6,6 +6,7 @@ export { urlSearchParamsBodySerializer, } from '../../client-core/bundle/bodySerializer'; export { buildClientParams } from '../../client-core/bundle/params'; +export { serializeQueryKeyValue } from '../../client-core/bundle/queryKeySerializer'; export { createClient } from './client'; export type { Client, diff --git a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/bundle/index.ts b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/bundle/index.ts index a237c773b0..c24fdc3b18 100644 --- a/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/bundle/index.ts +++ b/packages/openapi-ts/src/plugins/@hey-api/client-ofetch/bundle/index.ts @@ -6,6 +6,7 @@ export { urlSearchParamsBodySerializer, } from '../../client-core/bundle/bodySerializer'; export { buildClientParams } from '../../client-core/bundle/params'; +export { serializeQueryKeyValue } from '../../client-core/bundle/queryKeySerializer'; export { createClient } from './client'; export type { Client, diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/api.ts b/packages/openapi-ts/src/plugins/@pinia/colada/api.ts index 8b41d45a35..171c8310c7 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/api.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/api.ts @@ -6,8 +6,10 @@ type SelectorType = | '_JSONValue' | 'AxiosError' | 'createQueryKey' - | 'queryOptionsFn' + | 'defineQueryOptions' | 'QueryKey' + | 'queryOptionsFn' + | 'serializeQueryKeyValue' | 'UseMutationOptions' | 'UseQueryOptions'; @@ -18,8 +20,10 @@ export type IApi = { * - `_JSONValue`: never * - `AxiosError`: never * - `createQueryKey`: never - * - `queryOptionsFn`: `operation.id` string + * - `defineQueryOptions`: never * - `QueryKey`: never + * - `queryOptionsFn`: `operation.id` string + * - `serializeQueryKeyValue`: never * - `UseMutationOptions`: never * - `UseQueryOptions`: never * @returns Selector array diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts b/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts index 6903791614..ea619179ed 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/queryKey.ts @@ -1,6 +1,7 @@ import type { Symbol } from '@hey-api/codegen-core'; import type { Expression } from 'typescript'; +import { clientFolderAbsolutePath } from '../../../generate/client'; import { hasOperationDataRequired } from '../../../ir/operation'; import type { IR } from '../../../ir/types'; import { buildName } from '../../../openApi/shared/utils/name'; @@ -34,6 +35,9 @@ export const createQueryKeyFunction = ({ const symbolQueryKeyType = plugin.referenceSymbol( plugin.api.getSelector('QueryKey'), ); + const symbolJsonValue = plugin.referenceSymbol( + plugin.api.getSelector('_JSONValue'), + ); const returnType = tsc.indexedAccessTypeNode({ indexType: tsc.literalTypeNode({ @@ -60,6 +64,12 @@ export const createQueryKeyFunction = ({ ) : undefined; + const clientModule = clientFolderAbsolutePath(plugin.context.config); + const symbolSerializeQueryValue = plugin.registerSymbol({ + external: clientModule, + name: 'serializeQueryKeyValue', + }); + const fn = tsc.constVariable({ expression: tsc.arrowFunction({ multiLine: true, @@ -88,10 +98,7 @@ export const createQueryKeyFunction = ({ expression: tsc.objectExpression({ multiLine: false, obj: [ - { - key: '_id', - value: tsc.identifier({ text: 'id' }), - }, + { key: '_id', value: tsc.identifier({ text: 'id' }) }, { key: baseUrlKey, value: tsc.identifier({ @@ -118,7 +125,9 @@ export const createQueryKeyFunction = ({ expression: tsc.identifier({ text: 'tags' }), type: tsc.keywordTypeNode({ keyword: 'unknown' }), }), - type: tsc.keywordTypeNode({ keyword: 'undefined' }), + type: tsc.typeReferenceNode({ + typeName: symbolJsonValue.placeholder, + }), }), }), }), @@ -126,52 +135,47 @@ export const createQueryKeyFunction = ({ }), }), tsc.ifStatement({ - expression: tsc.propertyAccessExpression({ - expression: optionsIdentifier, - isOptional: true, - name: tsc.identifier({ text: 'body' }), + expression: tsc.binaryExpression({ + left: tsc.propertyAccessExpression({ + expression: optionsIdentifier, + isOptional: true, + name: tsc.identifier({ text: 'body' }), + }), + operator: '!==', + right: 'undefined', }), thenStatement: tsc.block({ statements: [ - tsc.expressionToStatement({ - expression: tsc.binaryExpression({ - left: tsc.propertyAccessExpression({ - expression: 'params', - name: 'body', - }), - right: tsc.propertyAccessExpression({ - expression: 'options', - name: 'body', - }), + tsc.constVariable({ + expression: tsc.callExpression({ + functionName: symbolSerializeQueryValue.placeholder, + parameters: [ + tsc.propertyAccessExpression({ + expression: 'options', + name: 'body', + }), + ], }), + name: 'normalizedBody', }), - ], - }), - }), - tsc.ifStatement({ - expression: tsc.propertyAccessExpression({ - expression: optionsIdentifier, - isOptional: true, - name: tsc.identifier({ text: 'headers' }), - }), - thenStatement: tsc.block({ - statements: [ - tsc.expressionToStatement({ + tsc.ifStatement({ expression: tsc.binaryExpression({ - left: tsc.propertyAccessExpression({ - expression: 'params', - name: 'headers', - }), - right: tsc.asExpression({ - expression: tsc.asExpression({ - expression: tsc.propertyAccessExpression({ - expression: 'options', - name: 'headers', + left: tsc.identifier({ text: 'normalizedBody' }), + operator: '!==', + right: 'undefined', + }), + thenStatement: tsc.block({ + statements: [ + tsc.expressionToStatement({ + expression: tsc.binaryExpression({ + left: tsc.propertyAccessExpression({ + expression: 'params', + name: 'body', + }), + right: tsc.identifier({ text: 'normalizedBody' }), }), - type: tsc.keywordTypeNode({ keyword: 'unknown' }), }), - type: tsc.keywordTypeNode({ keyword: 'undefined' }), - }), + ], }), }), ], @@ -201,29 +205,47 @@ export const createQueryKeyFunction = ({ }), }), tsc.ifStatement({ - expression: tsc.propertyAccessExpression({ - expression: optionsIdentifier, - isOptional: true, - name: tsc.identifier({ text: 'query' }), + expression: tsc.binaryExpression({ + left: tsc.propertyAccessExpression({ + expression: optionsIdentifier, + isOptional: true, + name: tsc.identifier({ text: 'query' }), + }), + operator: '!==', + right: 'undefined', }), thenStatement: tsc.block({ statements: [ - tsc.expressionToStatement({ + tsc.constVariable({ + expression: tsc.callExpression({ + functionName: symbolSerializeQueryValue.placeholder, + parameters: [ + tsc.propertyAccessExpression({ + expression: 'options', + name: 'query', + }), + ], + }), + name: 'normalizedQuery', + }), + tsc.ifStatement({ expression: tsc.binaryExpression({ - left: tsc.propertyAccessExpression({ - expression: 'params', - name: 'query', - }), - right: tsc.asExpression({ - expression: tsc.asExpression({ - expression: tsc.propertyAccessExpression({ - expression: 'options', - name: 'query', + left: tsc.identifier({ text: 'normalizedQuery' }), + operator: '!==', + right: 'undefined', + }), + thenStatement: tsc.block({ + statements: [ + tsc.expressionToStatement({ + expression: tsc.binaryExpression({ + left: tsc.propertyAccessExpression({ + expression: 'params', + name: 'query', + }), + right: tsc.identifier({ text: 'normalizedQuery' }), }), - type: tsc.keywordTypeNode({ keyword: 'unknown' }), }), - type: tsc.keywordTypeNode({ keyword: 'undefined' }), - }), + ], }), }), ], @@ -286,10 +308,7 @@ export const createQueryKeyType = ({ ); const properties: Array = [ - { - name: '_id', - type: tsc.keywordTypeNode({ keyword: 'string' }), - }, + { name: '_id', type: tsc.keywordTypeNode({ keyword: 'string' }) }, { isRequired: false, name: getClientBaseUrlKey(plugin.context.config), @@ -297,7 +316,7 @@ export const createQueryKeyType = ({ }, { isRequired: false, - name: 'headers', + name: 'body', type: tsc.typeReferenceNode({ typeName: symbolJsonValue.placeholder }), }, { @@ -318,9 +337,7 @@ export const createQueryKeyType = ({ ); const symbolQueryKeyType = plugin.registerSymbol({ exported: true, - meta: { - kind: 'type', - }, + meta: { kind: 'type' }, name: 'QueryKey', selector: plugin.api.getSelector('QueryKey'), }); @@ -332,7 +349,7 @@ export const createQueryKeyType = ({ tsc.typeIntersectionNode({ types: [ tsc.typeReferenceNode({ - typeName: `Pick<${TOptionsType}, 'body' | 'path'>`, + typeName: `Pick<${TOptionsType}, 'path'>`, }), tsc.typeInterfaceNode({ properties, diff --git a/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts b/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts index 6449279b57..2e0ef5a0cd 100644 --- a/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts +++ b/packages/openapi-ts/src/plugins/@pinia/colada/queryOptions.ts @@ -15,11 +15,11 @@ import { queryKeyStatement, } from './queryKey'; import type { PiniaColadaPlugin } from './types'; -import { useTypeData, useTypeError, useTypeResponse } from './useType'; +import { useTypeData } from './useType'; import { getPublicTypeData } from './utils'; -const fnOptions = 'context'; const optionsParamName = 'options'; +const fnOptions = 'context'; export const createQueryOptions = ({ operation, @@ -38,38 +38,56 @@ export const createQueryOptions = ({ context: plugin.context, operation, }); - if (!plugin.getSymbol(plugin.api.getSelector('createQueryKey'))) { createQueryKeyType({ plugin }); createQueryKeyFunction({ plugin }); } - const symbolUseQueryOptions = plugin.referenceSymbol( - plugin.api.getSelector('UseQueryOptions'), - ); - - const symbolQueryKey = plugin.registerSymbol({ - exported: true, - name: buildName({ - config: plugin.config.queryKeys, - name: operation.id, - }), - }); - const node = queryKeyStatement({ - operation, - plugin, - symbol: symbolQueryKey, - }); - plugin.setSymbolValue(symbolQueryKey, node); + let keyExpression: ts.Expression; + if (plugin.config.queryKeys.enabled) { + const symbolQueryKey = plugin.registerSymbol({ + exported: true, + name: buildName({ + config: plugin.config.queryKeys, + name: operation.id, + }), + }); + const node = queryKeyStatement({ + operation, + plugin, + symbol: symbolQueryKey, + }); + plugin.setSymbolValue(symbolQueryKey, node); + keyExpression = tsc.callExpression({ + functionName: symbolQueryKey.placeholder, + parameters: [optionsParamName], + }); + } else { + const symbolCreateQueryKey = plugin.referenceSymbol( + plugin.api.getSelector('createQueryKey'), + ); + // Optionally include tags when configured + let tagsExpr: ts.Expression | undefined; + if ( + plugin.config.queryKeys.tags && + operation.tags && + operation.tags.length > 0 + ) { + tagsExpr = tsc.arrayLiteralExpression({ + elements: operation.tags.map((t) => tsc.stringLiteral({ text: t })), + }); + } + keyExpression = tsc.callExpression({ + functionName: symbolCreateQueryKey.placeholder, + parameters: [tsc.ots.string(operation.id), optionsParamName, tagsExpr], + }); + } const typeData = useTypeData({ operation, plugin }); - const typeError = useTypeError({ operation, plugin }); - const typeResponse = useTypeResponse({ operation, plugin }); - const { isNuxtClient, strippedTypeData } = getPublicTypeData({ + const { strippedTypeData } = getPublicTypeData({ plugin, typeData, }); - const awaitSdkExpression = tsc.awaitExpression({ expression: tsc.callExpression({ functionName: queryFn, @@ -77,16 +95,9 @@ export const createQueryOptions = ({ tsc.objectExpression({ multiLine: true, obj: [ - { - spread: optionsParamName, - }, - { - spread: fnOptions, - }, - { - key: 'throwOnError', - value: true, - }, + { spread: optionsParamName }, + { spread: fnOptions }, + { key: 'throwOnError', value: true }, ], }), ], @@ -94,7 +105,6 @@ export const createQueryOptions = ({ }); const statements: Array = []; - if (plugin.getPluginOrThrow('@hey-api/sdk').config.responseStyle === 'data') { statements.push( tsc.returnVariable({ @@ -117,31 +127,20 @@ export const createQueryOptions = ({ const queryOptionsObj: Array<{ key: string; value: ts.Expression }> = [ { key: 'key', - value: tsc.callExpression({ - functionName: symbolQueryKey.placeholder, - parameters: [optionsParamName], - }), + value: keyExpression, }, { key: 'query', value: tsc.arrowFunction({ async: true, multiLine: true, - parameters: [ - isNuxtClient - ? { - name: fnOptions, - type: strippedTypeData, - } - : { name: fnOptions }, - ], + parameters: [{ name: fnOptions }], statements, }), }, ]; const meta = handleMeta(plugin, operation, 'queryOptions'); - if (meta) { queryOptionsObj.push({ key: 'meta', @@ -157,28 +156,28 @@ export const createQueryOptions = ({ }), selector: plugin.api.getSelector('queryOptionsFn', operation.id), }); + const symbolDefineQueryOptions = plugin.registerSymbol({ + external: plugin.name, + name: 'defineQueryOptions', + selector: plugin.api.getSelector('defineQueryOptions'), + }); const statement = tsc.constVariable({ comment: plugin.config.comments ? createOperationComment({ operation }) : undefined, exportConst: symbolQueryOptionsFn.exported, - expression: tsc.arrowFunction({ + expression: tsc.callExpression({ + functionName: symbolDefineQueryOptions.placeholder, parameters: [ - { - isRequired: isRequiredOptions, - name: optionsParamName, - type: strippedTypeData, - }, - ], - // TODO: better types syntax - returnType: isNuxtClient - ? `${symbolUseQueryOptions.placeholder}<${typeResponse}, ${strippedTypeData}, ${typeError}>` - : `${symbolUseQueryOptions.placeholder}<${typeResponse}, ${typeError}>`, - statements: [ - tsc.returnStatement({ - expression: tsc.objectExpression({ - obj: queryOptionsObj, - }), + tsc.arrowFunction({ + parameters: [ + { + isRequired: isRequiredOptions, + name: optionsParamName, + type: strippedTypeData, + }, + ], + statements: tsc.objectExpression({ obj: queryOptionsObj }), }), ], }), diff --git a/packages/openapi-ts/src/tsc/transform.ts b/packages/openapi-ts/src/tsc/transform.ts index 1ae12e14bd..f6f3253e83 100644 --- a/packages/openapi-ts/src/tsc/transform.ts +++ b/packages/openapi-ts/src/tsc/transform.ts @@ -67,7 +67,7 @@ export const createBinaryExpression = ({ right, }: { left: ts.Expression; - operator?: '=' | '===' | 'in' | '??'; + operator?: '=' | '===' | '!==' | 'in' | '??'; right: ts.Expression | string; }) => { const expression = ts.factory.createBinaryExpression( @@ -77,9 +77,11 @@ export const createBinaryExpression = ({ ? ts.SyntaxKind.EqualsToken : operator === '===' ? ts.SyntaxKind.EqualsEqualsEqualsToken - : operator === '??' - ? ts.SyntaxKind.QuestionQuestionToken - : ts.SyntaxKind.InKeyword, + : operator === '!==' + ? ts.SyntaxKind.ExclamationEqualsEqualsToken + : operator === '??' + ? ts.SyntaxKind.QuestionQuestionToken + : ts.SyntaxKind.InKeyword, typeof right === 'string' ? createIdentifier({ text: right }) : right, ); return expression;