301 lines
7.2 KiB
TypeScript
301 lines
7.2 KiB
TypeScript
// This file is auto-generated by @hey-api/openapi-ts
|
|
|
|
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,
|
|
createConfig,
|
|
createInterceptors,
|
|
getParseAs,
|
|
mergeConfigs,
|
|
mergeHeaders,
|
|
setAuthParams,
|
|
} from './utils.gen';
|
|
|
|
type ReqInit = Omit<RequestInit, 'body' | 'headers'> & {
|
|
body?: any;
|
|
headers: ReturnType<typeof mergeHeaders>;
|
|
};
|
|
|
|
export const createClient = (config: Config = {}): Client => {
|
|
let _config = mergeConfigs(createConfig(), config);
|
|
|
|
const getConfig = (): Config => ({ ..._config });
|
|
|
|
const setConfig = (config: Config): Config => {
|
|
_config = mergeConfigs(_config, config);
|
|
return getConfig();
|
|
};
|
|
|
|
const interceptors = createInterceptors<
|
|
Request,
|
|
Response,
|
|
unknown,
|
|
ResolvedRequestOptions
|
|
>();
|
|
|
|
const beforeRequest = async (options: RequestOptions) => {
|
|
const opts = {
|
|
..._config,
|
|
...options,
|
|
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
|
|
headers: mergeHeaders(_config.headers, options.headers),
|
|
serializedBody: undefined,
|
|
};
|
|
|
|
if (opts.security) {
|
|
await setAuthParams({
|
|
...opts,
|
|
security: opts.security,
|
|
});
|
|
}
|
|
|
|
if (opts.requestValidator) {
|
|
await opts.requestValidator(opts);
|
|
}
|
|
|
|
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.body === undefined || opts.serializedBody === '') {
|
|
opts.headers.delete('Content-Type');
|
|
}
|
|
|
|
const url = buildUrl(opts);
|
|
|
|
return { opts, url };
|
|
};
|
|
|
|
const request: Client['request'] = async (options) => {
|
|
// @ts-expect-error
|
|
const { opts, url } = await beforeRequest(options);
|
|
const requestInit: ReqInit = {
|
|
redirect: 'follow',
|
|
...opts,
|
|
body: getValidRequestBody(opts),
|
|
};
|
|
|
|
let request = new Request(url, requestInit);
|
|
|
|
for (const fn of interceptors.request.fns) {
|
|
if (fn) {
|
|
request = await fn(request, opts);
|
|
}
|
|
}
|
|
|
|
// fetch must be assigned here, otherwise it would throw the error:
|
|
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
|
|
const _fetch = opts.fetch!;
|
|
let response: Response;
|
|
|
|
try {
|
|
response = await _fetch(request);
|
|
} catch (error) {
|
|
// Handle fetch exceptions (AbortError, network errors, etc.)
|
|
let finalError = error;
|
|
|
|
for (const fn of interceptors.error.fns) {
|
|
if (fn) {
|
|
finalError = (await fn(
|
|
error,
|
|
undefined as any,
|
|
request,
|
|
opts,
|
|
)) as unknown;
|
|
}
|
|
}
|
|
|
|
finalError = finalError || ({} as unknown);
|
|
|
|
if (opts.throwOnError) {
|
|
throw finalError;
|
|
}
|
|
|
|
// Return error response
|
|
return opts.responseStyle === 'data'
|
|
? undefined
|
|
: {
|
|
error: finalError,
|
|
request,
|
|
response: undefined as any,
|
|
};
|
|
}
|
|
|
|
for (const fn of interceptors.response.fns) {
|
|
if (fn) {
|
|
response = await fn(response, request, opts);
|
|
}
|
|
}
|
|
|
|
const result = {
|
|
request,
|
|
response,
|
|
};
|
|
|
|
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: emptyData,
|
|
...result,
|
|
};
|
|
}
|
|
|
|
let data: any;
|
|
switch (parseAs) {
|
|
case 'arrayBuffer':
|
|
case 'blob':
|
|
case 'formData':
|
|
case 'json':
|
|
case 'text':
|
|
data = await response[parseAs]();
|
|
break;
|
|
case 'stream':
|
|
return opts.responseStyle === 'data'
|
|
? response.body
|
|
: {
|
|
data: response.body,
|
|
...result,
|
|
};
|
|
}
|
|
|
|
if (parseAs === 'json') {
|
|
if (opts.responseValidator) {
|
|
await opts.responseValidator(data);
|
|
}
|
|
|
|
if (opts.responseTransformer) {
|
|
data = await opts.responseTransformer(data);
|
|
}
|
|
}
|
|
|
|
return opts.responseStyle === 'data'
|
|
? data
|
|
: {
|
|
data,
|
|
...result,
|
|
};
|
|
}
|
|
|
|
const textError = await response.text();
|
|
let jsonError: unknown;
|
|
|
|
try {
|
|
jsonError = JSON.parse(textError);
|
|
} catch {
|
|
// noop
|
|
}
|
|
|
|
const error = jsonError ?? textError;
|
|
let finalError = error;
|
|
|
|
for (const fn of interceptors.error.fns) {
|
|
if (fn) {
|
|
finalError = (await fn(error, response, request, opts)) as string;
|
|
}
|
|
}
|
|
|
|
finalError = finalError || ({} as string);
|
|
|
|
if (opts.throwOnError) {
|
|
throw finalError;
|
|
}
|
|
|
|
// TODO: we probably want to return error and improve types
|
|
return opts.responseStyle === 'data'
|
|
? undefined
|
|
: {
|
|
error: finalError,
|
|
...result,
|
|
};
|
|
};
|
|
|
|
const makeMethodFn =
|
|
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
|
|
request({ ...options, method });
|
|
|
|
const makeSseFn =
|
|
(method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
|
|
const { opts, url } = await beforeRequest(options);
|
|
return createSseClient({
|
|
...opts,
|
|
body: opts.body as BodyInit | null | undefined,
|
|
headers: opts.headers as unknown as Record<string, string>,
|
|
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,
|
|
});
|
|
};
|
|
|
|
return {
|
|
buildUrl,
|
|
connect: makeMethodFn('CONNECT'),
|
|
delete: makeMethodFn('DELETE'),
|
|
get: makeMethodFn('GET'),
|
|
getConfig,
|
|
head: makeMethodFn('HEAD'),
|
|
interceptors,
|
|
options: makeMethodFn('OPTIONS'),
|
|
patch: makeMethodFn('PATCH'),
|
|
post: makeMethodFn('POST'),
|
|
put: makeMethodFn('PUT'),
|
|
request,
|
|
setConfig,
|
|
sse: {
|
|
connect: makeSseFn('CONNECT'),
|
|
delete: makeSseFn('DELETE'),
|
|
get: makeSseFn('GET'),
|
|
head: makeSseFn('HEAD'),
|
|
options: makeSseFn('OPTIONS'),
|
|
patch: makeSseFn('PATCH'),
|
|
post: makeSseFn('POST'),
|
|
put: makeSseFn('PUT'),
|
|
trace: makeSseFn('TRACE'),
|
|
},
|
|
trace: makeMethodFn('TRACE'),
|
|
} as Client;
|
|
};
|