Something went wrong. Try again.
fork of hey-api/openapi-ts because I need some additional things
Something went wrong. Try again.
10 kB · 432 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433import { getAuthToken } from './core/auth';import type { QuerySerializer, QuerySerializerOptions,} from './core/bodySerializer';import { jsonBodySerializer } from './core/bodySerializer';import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam,} from './core/pathSerializer';import type { Client, ClientOptions, Config, RequestOptions } from './types';
interface PathSerializer { path: Record<string, unknown>; url: string;}
const PATH_PARAM_RE = /\{[^{}]+\}/g;
type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';type MatrixStyle = 'label' | 'matrix' | 'simple';type ArraySeparatorStyle = ArrayStyle | MatrixStyle;
const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { let url = _url; const matches = _url.match(PATH_PARAM_RE); if (matches) { for (const match of matches) { let explode = false; let name = match.substring(1, match.length - 1); let style: ArraySeparatorStyle = 'simple';
if (name.endsWith('*')) { explode = true; name = name.substring(0, name.length - 1); }
if (name.startsWith('.')) { name = name.substring(1); style = 'label'; } else if (name.startsWith(';')) { name = name.substring(1); style = 'matrix'; }
const value = path[name];
if (value === undefined || value === null) { continue; }
if (Array.isArray(value)) { url = url.replace( match, serializeArrayParam({ explode, name, style, value }), ); continue; }
if (typeof value === 'object') { url = url.replace( match, serializeObjectParam({ explode, name, style, value: value as Record<string, unknown>, valueOnly: true, }), ); continue; }
if (style === 'matrix') { url = url.replace( match, `;${serializePrimitiveParam({ name, value: value as string, })}`, ); continue; }
const replaceValue = encodeURIComponent( style === 'label' ? `.${value as string}` : (value as string), ); url = url.replace(match, replaceValue); } } return url;};
export const createQuerySerializer = <T = unknown>({ allowReserved, array, object,}: QuerySerializerOptions = {}) => { const querySerializer = (queryParams: T) => { const search: string[] = []; if (queryParams && typeof queryParams === 'object') { for (const name in queryParams) { const value = queryParams[name];
if (value === undefined || value === null) { continue; }
if (Array.isArray(value)) { const serializedArray = serializeArrayParam({ allowReserved, explode: true, name, style: 'form', value, ...array, }); if (serializedArray) search.push(serializedArray); } else if (typeof value === 'object') { const serializedObject = serializeObjectParam({ allowReserved, explode: true, name, style: 'deepObject', value: value as Record<string, unknown>, ...object, }); if (serializedObject) search.push(serializedObject); } else { const serializedPrimitive = serializePrimitiveParam({ allowReserved, name, value: value as string, }); if (serializedPrimitive) search.push(serializedPrimitive); } } } return search.join('&'); }; return querySerializer;};
/** * Infers parseAs value from provided Content-Type header. */export const getParseAs = ( contentType: string | null,): Exclude<Config['parseAs'], 'auto'> => { if (!contentType) { // If no Content-Type header is provided, the best we can do is return the raw response body, // which is effectively the same as the 'stream' option. return 'stream'; }
const cleanContent = contentType.split(';')[0]?.trim();
if (!cleanContent) { return; }
if ( cleanContent.startsWith('application/json') || cleanContent.endsWith('+json') ) { return 'json'; }
if (cleanContent === 'multipart/form-data') { return 'formData'; }
if ( ['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type), ) ) { return 'blob'; }
if (cleanContent.startsWith('text/')) { return 'text'; }
return;};
const checkForExistence = ( options: Pick<RequestOptions, 'auth' | 'query'> & { headers: Headers; }, name?: string,): boolean => { if (!name) { return false; } if ( options.headers.has(name) || options.query?.[name] || options.headers.get('Cookie')?.includes(`${name}=`) ) { return true; } return false;};
export const setAuthParams = async ({ security, ...options}: Pick<Required<RequestOptions>, 'security'> & Pick<RequestOptions, 'auth' | 'query'> & { headers: Headers; }) => { for (const auth of security) { if (checkForExistence(options, auth.name)) { continue; }
const token = await getAuthToken(auth, options.auth);
if (!token) { continue; }
const name = auth.name ?? 'Authorization';
switch (auth.in) { case 'query': if (!options.query) { options.query = {}; } options.query[name] = token; break; case 'cookie': options.headers.append('Cookie', `${name}=${token}`); break; case 'header': default: options.headers.set(name, token); break; } }};
export const buildUrl: Client['buildUrl'] = (options) => { const url = getUrl({ baseUrl: options.baseUrl as string, path: options.path, query: options.query, querySerializer: typeof options.querySerializer === 'function' ? options.querySerializer : createQuerySerializer(options.querySerializer), url: options.url, }); return url;};
export const getUrl = ({ baseUrl, path, query, querySerializer, url: _url,}: { baseUrl?: string; path?: Record<string, unknown>; query?: Record<string, unknown>; querySerializer: QuerySerializer; url: string;}) => { const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; let url = (baseUrl ?? '') + pathUrl; if (path) { url = defaultPathSerializer({ path, url }); } let search = query ? querySerializer(query) : ''; if (search.startsWith('?')) { search = search.substring(1); } if (search) { url += `?${search}`; } return url;};
export const mergeConfigs = (a: Config, b: Config): Config => { const config = { ...a, ...b }; if (config.baseUrl?.endsWith('/')) { config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); } config.headers = mergeHeaders(a.headers, b.headers); return config;};
export const mergeHeaders = ( ...headers: Array<Required<Config>['headers'] | undefined>): Headers => { const mergedHeaders = new Headers(); for (const header of headers) { if (!header || typeof header !== 'object') { continue; }
const iterator = header instanceof Headers ? header.entries() : Object.entries(header);
for (const [key, value] of iterator) { if (value === null) { mergedHeaders.delete(key); } else if (Array.isArray(value)) { for (const v of value) { mergedHeaders.append(key, v as string); } } else if (value !== undefined) { // assume object headers are meant to be JSON stringified, i.e. their // content value in OpenAPI specification is 'application/json' mergedHeaders.set( key, typeof value === 'object' ? JSON.stringify(value) : (value as string), ); } } } return mergedHeaders;};
type ErrInterceptor<Err, Res, Req, Options> = ( error: Err, response: Res, request: Req, options: Options,) => Err | Promise<Err>;
type ReqInterceptor<Req, Options> = ( request: Req, options: Options,) => Req | Promise<Req>;
type ResInterceptor<Res, Req, Options> = ( response: Res, request: Req, options: Options,) => Res | Promise<Res>;
class Interceptors<Interceptor> { fns: Array<Interceptor | null> = [];
clear(): void { this.fns = []; }
eject(id: number | Interceptor): void { const index = this.getInterceptorIndex(id); if (this.fns[index]) { this.fns[index] = null; } }
exists(id: number | Interceptor): boolean { const index = this.getInterceptorIndex(id); return Boolean(this.fns[index]); }
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, ): number | Interceptor | false { const index = this.getInterceptorIndex(id); if (this.fns[index]) { this.fns[index] = fn; return id; } return false; }
use(fn: Interceptor): number { this.fns.push(fn); return this.fns.length - 1; }}
export interface Middleware<Req, Res, Err, Options> { error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>; request: Interceptors<ReqInterceptor<Req, Options>>; response: Interceptors<ResInterceptor<Res, Req, Options>>;}
export const createInterceptors = <Req, Res, Err, Options>(): Middleware< Req, Res, Err, Options> => ({ error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(), request: new Interceptors<ReqInterceptor<Req, Options>>(), response: new Interceptors<ResInterceptor<Res, Req, Options>>(),});
const defaultQuerySerializer = createQuerySerializer({ allowReserved: false, array: { explode: true, style: 'form', }, object: { explode: true, style: 'deepObject', },});
const defaultHeaders = { 'Content-Type': 'application/json',};
export const createConfig = <T extends ClientOptions = ClientOptions>( override: Config<Omit<ClientOptions, keyof T> & T> = {},): Config<Omit<ClientOptions, keyof T> & T> => ({ ...jsonBodySerializer, headers: defaultHeaders, parseAs: 'auto', querySerializer: defaultQuerySerializer, ...override,});