Skip to content

Commit

Permalink
fix: disallow additional query parameters in experimental parser output
Browse files Browse the repository at this point in the history
  • Loading branch information
mrlubos committed Dec 5, 2024
1 parent 8dd356f commit ec48d32
Show file tree
Hide file tree
Showing 64 changed files with 2,044 additions and 397 deletions.
7 changes: 7 additions & 0 deletions .changeset/rude-nails-know.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@hey-api/client-axios': patch
'@hey-api/client-fetch': patch
'@hey-api/openapi-ts': patch
---

fix: disallow additional query parameters in experimental parser output
14 changes: 8 additions & 6 deletions packages/client-axios/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { AxiosError } from 'axios';
import type { AxiosError, RawAxiosRequestHeaders } from 'axios';
import axios from 'axios';

import type { Client, Config, RequestOptions } from './types';
import type { Client, Config } from './types';
import { createConfig, getUrl, mergeConfigs, mergeHeaders } from './utils';

export const createClient = (config: Config): Client => {
Expand All @@ -24,11 +24,13 @@ export const createClient = (config: Config): Client => {

// @ts-expect-error
const request: Client['request'] = async (options) => {
const opts: RequestOptions = {
const opts = {
..._config,
...options,
// @ts-expect-error
headers: mergeHeaders(_config.headers, options.headers),
headers: mergeHeaders(
_config.headers,
options.headers,
) as RawAxiosRequestHeaders,
};
if (opts.body && opts.bodySerializer) {
opts.body = opts.bodySerializer(opts.body);
Expand Down Expand Up @@ -90,7 +92,7 @@ export type {
Config,
Options,
OptionsLegacyParser,
RequestOptionsBase,
RequestOptions,
RequestResult,
} from './types';
export {
Expand Down
85 changes: 32 additions & 53 deletions packages/client-axios/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
AxiosStatic,
CreateAxiosDefaults,
Expand All @@ -19,12 +18,6 @@ export interface Config<ThrowOnError extends boolean = boolean>
* @default axios
*/
axios?: AxiosStatic;
/**
* Any body that you want to add to your request.
*
* {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
*/
body?: unknown;
/**
* A function for serializing request body parameter. By default,
* {@link JSON.stringify()} will be used.
Expand Down Expand Up @@ -76,10 +69,22 @@ export interface Config<ThrowOnError extends boolean = boolean>
throwOnError?: ThrowOnError;
}

export interface RequestOptionsBase<
ThrowOnError extends boolean,
export interface RequestOptions<
ThrowOnError extends boolean = false,
Url extends string = string,
> extends Config<ThrowOnError> {
/**
* Any body that you want to add to your request.
*
* {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
*/
body?: unknown;
/**
* You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a
* custom client.
*/
client?: Client;
path?: Record<string, unknown>;
query?: Record<string, unknown>;
url: Url;
Expand All @@ -101,16 +106,16 @@ type MethodFn = <
TError = unknown,
ThrowOnError extends boolean = false,
>(
options: Omit<RequestOptionsBase<ThrowOnError>, 'method'>,
options: Omit<RequestOptions<ThrowOnError>, 'method'>,
) => RequestResult<Data, TError, ThrowOnError>;

type RequestFn = <
Data = unknown,
TError = unknown,
ThrowOnError extends boolean = false,
>(
options: Omit<RequestOptionsBase<ThrowOnError>, 'method'> &
Pick<Required<RequestOptionsBase<ThrowOnError>>, 'method'>,
options: Omit<RequestOptions<ThrowOnError>, 'method'> &
Pick<Required<RequestOptions<ThrowOnError>>, 'method'>,
) => RequestResult<Data, TError, ThrowOnError>;

export interface Client {
Expand All @@ -127,49 +132,23 @@ export interface Client {
setConfig: (config: Config) => Config;
}

export type RequestOptions = RequestOptionsBase<false> &
Config<false> & {
headers: AxiosRequestConfig['headers'];
};

type OptionsBase<ThrowOnError extends boolean> = Omit<
RequestOptionsBase<ThrowOnError>,
'url'
> & {
/**
* You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a
* custom client.
*/
client?: Client;
};

export type Options<
T extends { url: string } = { url: string },
Data extends { url: string } = { url: string },
ThrowOnError extends boolean = boolean,
> = T extends { body?: any }
? T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'body' | 'headers'> & Omit<T, 'url'>
: OmitKeys<OptionsBase<ThrowOnError>, 'body'> &
Omit<T, 'url'> &
Pick<OptionsBase<ThrowOnError>, 'headers'>
: T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'headers'> &
Omit<T, 'url'> &
Pick<OptionsBase<ThrowOnError>, 'body'>
: OptionsBase<ThrowOnError> & Omit<T, 'url'>;
> = OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'path' | 'query' | 'url'> &
Omit<Data, 'url'>;

export type OptionsLegacyParser<
T = unknown,
Data = unknown,
ThrowOnError extends boolean = boolean,
> = T extends { body?: any }
? T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'body' | 'headers'> & T
: OmitKeys<OptionsBase<ThrowOnError>, 'body'> &
T &
Pick<OptionsBase<ThrowOnError>, 'headers'>
: T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'headers'> &
T &
Pick<OptionsBase<ThrowOnError>, 'body'>
: OptionsBase<ThrowOnError> & T;
> = Data extends { body?: any }
? Data extends { headers?: any }
? OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'headers' | 'url'> & Data
: OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'url'> &
Data &
Pick<RequestOptions<ThrowOnError>, 'headers'>
: Data extends { headers?: any }
? OmitKeys<RequestOptions<ThrowOnError>, 'headers' | 'url'> &
Data &
Pick<RequestOptions<ThrowOnError>, 'body'>
: OmitKeys<RequestOptions<ThrowOnError>, 'url'> & Data;
5 changes: 2 additions & 3 deletions packages/client-fetch/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ export const createClient = (config: Config = {}): Client => {

// @ts-expect-error
const request: Client['request'] = async (options) => {
// @ts-expect-error
const opts: RequestOptions = {
const opts = {
..._config,
...options,
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
Expand Down Expand Up @@ -170,7 +169,7 @@ export type {
Config,
Options,
OptionsLegacyParser,
RequestOptionsBase,
RequestOptions,
RequestResult,
} from './types';
export {
Expand Down
105 changes: 47 additions & 58 deletions packages/client-fetch/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,6 @@ export interface Config<ThrowOnError extends boolean = boolean>
* @default ''
*/
baseUrl?: string;
/**
* Any body that you want to add to your request.
*
* {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
*/
body?:
| RequestInit['body']
| Record<string, unknown>
| Array<Record<string, unknown>>
| Array<unknown>
| number;
/**
* A function for serializing request body parameter. By default,
* {@link JSON.stringify()} will be used.
Expand Down Expand Up @@ -98,10 +87,27 @@ export interface Config<ThrowOnError extends boolean = boolean>
throwOnError?: ThrowOnError;
}

export interface RequestOptionsBase<
ThrowOnError extends boolean,
export interface RequestOptions<
ThrowOnError extends boolean = boolean,
Url extends string = string,
> extends Config<ThrowOnError> {
/**
* Any body that you want to add to your request.
*
* {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
*/
body?:
| RequestInit['body']
| Record<string, unknown>
| Array<Record<string, unknown>>
| Array<unknown>
| number;
/**
* You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a
* custom client.
*/
client?: Client;
path?: Record<string, unknown>;
query?: Record<string, unknown>;
url: Url;
Expand Down Expand Up @@ -132,16 +138,16 @@ type MethodFn = <
TError = unknown,
ThrowOnError extends boolean = false,
>(
options: Omit<RequestOptionsBase<ThrowOnError>, 'method'>,
options: Omit<RequestOptions<ThrowOnError>, 'method'>,
) => RequestResult<Data, TError, ThrowOnError>;

type RequestFn = <
Data = unknown,
TError = unknown,
ThrowOnError extends boolean = false,
>(
options: Omit<RequestOptionsBase<ThrowOnError>, 'method'> &
Pick<Required<RequestOptionsBase<ThrowOnError>>, 'method'>,
options: Omit<RequestOptions<ThrowOnError>, 'method'> &
Pick<Required<RequestOptions<ThrowOnError>>, 'method'>,
) => RequestResult<Data, TError, ThrowOnError>;

export interface Client<
Expand All @@ -153,7 +159,16 @@ export interface Client<
/**
* Returns the final request URL. This method works only with experimental parser.
*/
buildUrl: <T extends { url: string }>(options: T & Options<T>) => string;
buildUrl: <
Data extends {
body?: unknown;
path?: Record<string, unknown>;
query?: Record<string, unknown>;
url: string;
},
>(
options: Pick<Data, 'url'> & Options<Data>,
) => string;
connect: MethodFn;
delete: MethodFn;
get: MethodFn;
Expand All @@ -169,49 +184,23 @@ export interface Client<
trace: MethodFn;
}

export type RequestOptions = RequestOptionsBase<false> &
Config<false> & {
headers: Headers;
};

type OptionsBase<ThrowOnError extends boolean> = Omit<
RequestOptionsBase<ThrowOnError>,
'url'
> & {
/**
* You can provide a client instance returned by `createClient()` instead of
* individual options. This might be also useful if you want to implement a
* custom client.
*/
client?: Client;
};

export type Options<
T extends { url: string } = { url: string },
Data extends { url: string } = { url: string },
ThrowOnError extends boolean = boolean,
> = T extends { body?: any }
? T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'body' | 'headers'> & Omit<T, 'url'>
: OmitKeys<OptionsBase<ThrowOnError>, 'body'> &
Omit<T, 'url'> &
Pick<OptionsBase<ThrowOnError>, 'headers'>
: T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'headers'> &
Omit<T, 'url'> &
Pick<OptionsBase<ThrowOnError>, 'body'>
: OptionsBase<ThrowOnError> & Omit<T, 'url'>;
> = OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'path' | 'query' | 'url'> &
Omit<Data, 'url'>;

export type OptionsLegacyParser<
T = unknown,
Data = unknown,
ThrowOnError extends boolean = boolean,
> = T extends { body?: any }
? T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'body' | 'headers'> & T
: OmitKeys<OptionsBase<ThrowOnError>, 'body'> &
T &
Pick<OptionsBase<ThrowOnError>, 'headers'>
: T extends { headers?: any }
? OmitKeys<OptionsBase<ThrowOnError>, 'headers'> &
T &
Pick<OptionsBase<ThrowOnError>, 'body'>
: OptionsBase<ThrowOnError> & T;
> = Data extends { body?: any }
? Data extends { headers?: any }
? OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'headers'> & Data
: OmitKeys<RequestOptions<ThrowOnError>, 'body'> &
Data &
Pick<RequestOptions<ThrowOnError>, 'headers'>
: Data extends { headers?: any }
? OmitKeys<RequestOptions<ThrowOnError>, 'headers'> &
Data &
Pick<RequestOptions<ThrowOnError>, 'body'>
: RequestOptions<ThrowOnError> & Data;
Loading

0 comments on commit ec48d32

Please sign in to comment.