fork of hey-api/openapi-ts because I need some additional things
1// This file is auto-generated by @hey-api/openapi-ts
2
3import { getAuthToken } from '../core/auth.gen';
4import type { QuerySerializerOptions } from '../core/bodySerializer.gen';
5import { jsonBodySerializer } from '../core/bodySerializer.gen';
6import {
7 serializeArrayParam,
8 serializeObjectParam,
9 serializePrimitiveParam,
10} from '../core/pathSerializer.gen';
11import { getUrl } from '../core/utils.gen';
12import type {
13 Client,
14 ClientOptions,
15 Config,
16 RequestOptions,
17} from './types.gen';
18
19export const createQuerySerializer = <T = unknown>({
20 parameters = {},
21 ...args
22}: QuerySerializerOptions = {}) => {
23 const querySerializer = (queryParams: T) => {
24 const search: string[] = [];
25 if (queryParams && typeof queryParams === 'object') {
26 for (const name in queryParams) {
27 const value = queryParams[name];
28
29 if (value === undefined || value === null) {
30 continue;
31 }
32
33 const options = parameters[name] || args;
34
35 if (Array.isArray(value)) {
36 const serializedArray = serializeArrayParam({
37 allowReserved: options.allowReserved,
38 explode: true,
39 name,
40 style: 'form',
41 value,
42 ...options.array,
43 });
44 if (serializedArray) search.push(serializedArray);
45 } else if (typeof value === 'object') {
46 const serializedObject = serializeObjectParam({
47 allowReserved: options.allowReserved,
48 explode: true,
49 name,
50 style: 'deepObject',
51 value: value as Record<string, unknown>,
52 ...options.object,
53 });
54 if (serializedObject) search.push(serializedObject);
55 } else {
56 const serializedPrimitive = serializePrimitiveParam({
57 allowReserved: options.allowReserved,
58 name,
59 value: value as string,
60 });
61 if (serializedPrimitive) search.push(serializedPrimitive);
62 }
63 }
64 }
65 return search.join('&');
66 };
67 return querySerializer;
68};
69
70/**
71 * Infers parseAs value from provided Content-Type header.
72 */
73export const getParseAs = (
74 contentType: string | null,
75): Exclude<Config['parseAs'], 'auto'> => {
76 if (!contentType) {
77 // If no Content-Type header is provided, the best we can do is return the raw response body,
78 // which is effectively the same as the 'stream' option.
79 return 'stream';
80 }
81
82 const cleanContent = contentType.split(';')[0]?.trim();
83
84 if (!cleanContent) {
85 return;
86 }
87
88 if (
89 cleanContent.startsWith('application/json') ||
90 cleanContent.endsWith('+json')
91 ) {
92 return 'json';
93 }
94
95 if (cleanContent === 'multipart/form-data') {
96 return 'formData';
97 }
98
99 if (
100 ['application/', 'audio/', 'image/', 'video/'].some((type) =>
101 cleanContent.startsWith(type),
102 )
103 ) {
104 return 'blob';
105 }
106
107 if (cleanContent.startsWith('text/')) {
108 return 'text';
109 }
110
111 return;
112};
113
114const checkForExistence = (
115 options: Pick<RequestOptions, 'auth' | 'query'> & {
116 headers: Headers;
117 },
118 name?: string,
119): boolean => {
120 if (!name) {
121 return false;
122 }
123 if (
124 options.headers.has(name) ||
125 options.query?.[name] ||
126 options.headers.get('Cookie')?.includes(`${name}=`)
127 ) {
128 return true;
129 }
130 return false;
131};
132
133export const setAuthParams = async ({
134 security,
135 ...options
136}: Pick<Required<RequestOptions>, 'security'> &
137 Pick<RequestOptions, 'auth' | 'query'> & {
138 headers: Headers;
139 }) => {
140 for (const auth of security) {
141 if (checkForExistence(options, auth.name)) {
142 continue;
143 }
144
145 const token = await getAuthToken(auth, options.auth);
146
147 if (!token) {
148 continue;
149 }
150
151 const name = auth.name ?? 'Authorization';
152
153 switch (auth.in) {
154 case 'query':
155 if (!options.query) {
156 options.query = {};
157 }
158 options.query[name] = token;
159 break;
160 case 'cookie':
161 options.headers.append('Cookie', `${name}=${token}`);
162 break;
163 case 'header':
164 default:
165 options.headers.set(name, token);
166 break;
167 }
168 }
169};
170
171export const buildUrl: Client['buildUrl'] = (options) =>
172 getUrl({
173 baseUrl: options.baseUrl as string,
174 path: options.path,
175 query: options.query,
176 querySerializer:
177 typeof options.querySerializer === 'function'
178 ? options.querySerializer
179 : createQuerySerializer(options.querySerializer),
180 url: options.url,
181 });
182
183export const mergeConfigs = (a: Config, b: Config): Config => {
184 const config = { ...a, ...b };
185 if (config.baseUrl?.endsWith('/')) {
186 config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
187 }
188 config.headers = mergeHeaders(a.headers, b.headers);
189 return config;
190};
191
192const headersEntries = (headers: Headers): Array<[string, string]> => {
193 const entries: Array<[string, string]> = [];
194 headers.forEach((value, key) => {
195 entries.push([key, value]);
196 });
197 return entries;
198};
199
200export const mergeHeaders = (
201 ...headers: Array<Required<Config>['headers'] | undefined>
202): Headers => {
203 const mergedHeaders = new Headers();
204 for (const header of headers) {
205 if (!header) {
206 continue;
207 }
208
209 const iterator =
210 header instanceof Headers
211 ? headersEntries(header)
212 : Object.entries(header);
213
214 for (const [key, value] of iterator) {
215 if (value === null) {
216 mergedHeaders.delete(key);
217 } else if (Array.isArray(value)) {
218 for (const v of value) {
219 mergedHeaders.append(key, v as string);
220 }
221 } else if (value !== undefined) {
222 // assume object headers are meant to be JSON stringified, i.e. their
223 // content value in OpenAPI specification is 'application/json'
224 mergedHeaders.set(
225 key,
226 typeof value === 'object' ? JSON.stringify(value) : (value as string),
227 );
228 }
229 }
230 }
231 return mergedHeaders;
232};
233
234type ErrInterceptor<Err, Res, Req, Options> = (
235 error: Err,
236 response: Res,
237 request: Req,
238 options: Options,
239) => Err | Promise<Err>;
240
241type ReqInterceptor<Req, Options> = (
242 request: Req,
243 options: Options,
244) => Req | Promise<Req>;
245
246type ResInterceptor<Res, Req, Options> = (
247 response: Res,
248 request: Req,
249 options: Options,
250) => Res | Promise<Res>;
251
252class Interceptors<Interceptor> {
253 fns: Array<Interceptor | null> = [];
254
255 clear(): void {
256 this.fns = [];
257 }
258
259 eject(id: number | Interceptor): void {
260 const index = this.getInterceptorIndex(id);
261 if (this.fns[index]) {
262 this.fns[index] = null;
263 }
264 }
265
266 exists(id: number | Interceptor): boolean {
267 const index = this.getInterceptorIndex(id);
268 return Boolean(this.fns[index]);
269 }
270
271 getInterceptorIndex(id: number | Interceptor): number {
272 if (typeof id === 'number') {
273 return this.fns[id] ? id : -1;
274 }
275 return this.fns.indexOf(id);
276 }
277
278 update(
279 id: number | Interceptor,
280 fn: Interceptor,
281 ): number | Interceptor | false {
282 const index = this.getInterceptorIndex(id);
283 if (this.fns[index]) {
284 this.fns[index] = fn;
285 return id;
286 }
287 return false;
288 }
289
290 use(fn: Interceptor): number {
291 this.fns.push(fn);
292 return this.fns.length - 1;
293 }
294}
295
296export interface Middleware<Req, Res, Err, Options> {
297 error: Interceptors<ErrInterceptor<Err, Res, Req, Options>>;
298 request: Interceptors<ReqInterceptor<Req, Options>>;
299 response: Interceptors<ResInterceptor<Res, Req, Options>>;
300}
301
302export const createInterceptors = <Req, Res, Err, Options>(): Middleware<
303 Req,
304 Res,
305 Err,
306 Options
307> => ({
308 error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(),
309 request: new Interceptors<ReqInterceptor<Req, Options>>(),
310 response: new Interceptors<ResInterceptor<Res, Req, Options>>(),
311});
312
313const defaultQuerySerializer = createQuerySerializer({
314 allowReserved: false,
315 array: {
316 explode: true,
317 style: 'form',
318 },
319 object: {
320 explode: true,
321 style: 'deepObject',
322 },
323});
324
325const defaultHeaders = {
326 'Content-Type': 'application/json',
327};
328
329export const createConfig = <T extends ClientOptions = ClientOptions>(
330 override: Config<Omit<ClientOptions, keyof T> & T> = {},
331): Config<Omit<ClientOptions, keyof T> & T> => ({
332 ...jsonBodySerializer,
333 headers: defaultHeaders,
334 parseAs: 'auto',
335 querySerializer: defaultQuerySerializer,
336 ...override,
337});