Skip to content

Commit b37b0e4

Browse files
autogen files under /client from openapi
1 parent 499a7c4 commit b37b0e4

File tree

12 files changed

+2813
-0
lines changed

12 files changed

+2813
-0
lines changed

frontend/eslint.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ const compat = new FlatCompat({
1010
});
1111

1212
const eslintConfig = [
13+
{
14+
ignores: ["src/client/**"], // ✅ Ignore generated OpenAPI client
15+
},
1316
...compat.extends("next/core-web-vitals", "next/typescript"),
1417
];
1518

frontend/openapi-ts.config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { defineConfig } from "@hey-api/openapi-ts"
2+
3+
export default defineConfig({
4+
input: "./openapi.json",
5+
output: "./src/client",
6+
7+
plugins: [
8+
"legacy/axios",
9+
{
10+
name: "@hey-api/sdk",
11+
// NOTE: this doesn't allow tree-shaking
12+
asClass: true,
13+
operationId: true,
14+
classNameBuilder: "{{name}}Service",
15+
methodNameBuilder: (operation) => {
16+
// @ts-expect-error
17+
let name: string = operation.name
18+
// @ts-expect-error
19+
const service: string = operation.service
20+
21+
if (service && name.toLowerCase().startsWith(service.toLowerCase())) {
22+
name = name.slice(service.length)
23+
}
24+
25+
return name.charAt(0).toLowerCase() + name.slice(1)
26+
},
27+
},
28+
{
29+
name: "@hey-api/schemas",
30+
type: "json",
31+
},
32+
],
33+
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { ApiRequestOptions } from './ApiRequestOptions';
2+
import type { ApiResult } from './ApiResult';
3+
4+
export class ApiError extends Error {
5+
public readonly url: string;
6+
public readonly status: number;
7+
public readonly statusText: string;
8+
public readonly body: unknown;
9+
public readonly request: ApiRequestOptions;
10+
11+
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
12+
super(message);
13+
14+
this.name = 'ApiError';
15+
this.url = response.url;
16+
this.status = response.status;
17+
this.statusText = response.statusText;
18+
this.body = response.body;
19+
this.request = request;
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export type ApiRequestOptions<T = unknown> = {
2+
readonly body?: any;
3+
readonly cookies?: Record<string, unknown>;
4+
readonly errors?: Record<number | string, string>;
5+
readonly formData?: Record<string, unknown> | any[] | Blob | File;
6+
readonly headers?: Record<string, unknown>;
7+
readonly mediaType?: string;
8+
readonly method:
9+
| 'DELETE'
10+
| 'GET'
11+
| 'HEAD'
12+
| 'OPTIONS'
13+
| 'PATCH'
14+
| 'POST'
15+
| 'PUT';
16+
readonly path?: Record<string, unknown>;
17+
readonly query?: Record<string, unknown>;
18+
readonly responseHeader?: string;
19+
readonly responseTransformer?: (data: unknown) => Promise<T>;
20+
readonly url: string;
21+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type ApiResult<TData = any> = {
2+
readonly body: TData;
3+
readonly ok: boolean;
4+
readonly status: number;
5+
readonly statusText: string;
6+
readonly url: string;
7+
};
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
export class CancelError extends Error {
2+
constructor(message: string) {
3+
super(message);
4+
this.name = 'CancelError';
5+
}
6+
7+
public get isCancelled(): boolean {
8+
return true;
9+
}
10+
}
11+
12+
export interface OnCancel {
13+
readonly isResolved: boolean;
14+
readonly isRejected: boolean;
15+
readonly isCancelled: boolean;
16+
17+
(cancelHandler: () => void): void;
18+
}
19+
20+
export class CancelablePromise<T> implements Promise<T> {
21+
private _isResolved: boolean;
22+
private _isRejected: boolean;
23+
private _isCancelled: boolean;
24+
readonly cancelHandlers: (() => void)[];
25+
readonly promise: Promise<T>;
26+
private _resolve?: (value: T | PromiseLike<T>) => void;
27+
private _reject?: (reason?: unknown) => void;
28+
29+
constructor(
30+
executor: (
31+
resolve: (value: T | PromiseLike<T>) => void,
32+
reject: (reason?: unknown) => void,
33+
onCancel: OnCancel
34+
) => void
35+
) {
36+
this._isResolved = false;
37+
this._isRejected = false;
38+
this._isCancelled = false;
39+
this.cancelHandlers = [];
40+
this.promise = new Promise<T>((resolve, reject) => {
41+
this._resolve = resolve;
42+
this._reject = reject;
43+
44+
const onResolve = (value: T | PromiseLike<T>): void => {
45+
if (this._isResolved || this._isRejected || this._isCancelled) {
46+
return;
47+
}
48+
this._isResolved = true;
49+
if (this._resolve) this._resolve(value);
50+
};
51+
52+
const onReject = (reason?: unknown): void => {
53+
if (this._isResolved || this._isRejected || this._isCancelled) {
54+
return;
55+
}
56+
this._isRejected = true;
57+
if (this._reject) this._reject(reason);
58+
};
59+
60+
const onCancel = (cancelHandler: () => void): void => {
61+
if (this._isResolved || this._isRejected || this._isCancelled) {
62+
return;
63+
}
64+
this.cancelHandlers.push(cancelHandler);
65+
};
66+
67+
Object.defineProperty(onCancel, 'isResolved', {
68+
get: (): boolean => this._isResolved,
69+
});
70+
71+
Object.defineProperty(onCancel, 'isRejected', {
72+
get: (): boolean => this._isRejected,
73+
});
74+
75+
Object.defineProperty(onCancel, 'isCancelled', {
76+
get: (): boolean => this._isCancelled,
77+
});
78+
79+
return executor(onResolve, onReject, onCancel as OnCancel);
80+
});
81+
}
82+
83+
get [Symbol.toStringTag](): string {
84+
return "Cancellable Promise";
85+
}
86+
87+
public then<TResult1 = T, TResult2 = never>(
88+
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
89+
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
90+
): Promise<TResult1 | TResult2> {
91+
return this.promise.then(onFulfilled, onRejected);
92+
}
93+
94+
public catch<TResult = never>(
95+
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null
96+
): Promise<T | TResult> {
97+
return this.promise.catch(onRejected);
98+
}
99+
100+
public finally(onFinally?: (() => void) | null): Promise<T> {
101+
return this.promise.finally(onFinally);
102+
}
103+
104+
public cancel(): void {
105+
if (this._isResolved || this._isRejected || this._isCancelled) {
106+
return;
107+
}
108+
this._isCancelled = true;
109+
if (this.cancelHandlers.length) {
110+
try {
111+
for (const cancelHandler of this.cancelHandlers) {
112+
cancelHandler();
113+
}
114+
} catch (error) {
115+
console.warn('Cancellation threw an error', error);
116+
return;
117+
}
118+
}
119+
this.cancelHandlers.length = 0;
120+
if (this._reject) this._reject(new CancelError('Request aborted'));
121+
}
122+
123+
public get isCancelled(): boolean {
124+
return this._isCancelled;
125+
}
126+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
2+
import type { ApiRequestOptions } from './ApiRequestOptions';
3+
4+
type Headers = Record<string, string>;
5+
type Middleware<T> = (value: T) => T | Promise<T>;
6+
type Resolver<T> = (options: ApiRequestOptions<T>) => Promise<T>;
7+
8+
export class Interceptors<T> {
9+
_fns: Middleware<T>[];
10+
11+
constructor() {
12+
this._fns = [];
13+
}
14+
15+
eject(fn: Middleware<T>): void {
16+
const index = this._fns.indexOf(fn);
17+
if (index !== -1) {
18+
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
19+
}
20+
}
21+
22+
use(fn: Middleware<T>): void {
23+
this._fns = [...this._fns, fn];
24+
}
25+
}
26+
27+
export type OpenAPIConfig = {
28+
BASE: string;
29+
CREDENTIALS: 'include' | 'omit' | 'same-origin';
30+
ENCODE_PATH?: ((path: string) => string) | undefined;
31+
HEADERS?: Headers | Resolver<Headers> | undefined;
32+
PASSWORD?: string | Resolver<string> | undefined;
33+
TOKEN?: string | Resolver<string> | undefined;
34+
USERNAME?: string | Resolver<string> | undefined;
35+
VERSION: string;
36+
WITH_CREDENTIALS: boolean;
37+
interceptors: {
38+
request: Interceptors<AxiosRequestConfig>;
39+
response: Interceptors<AxiosResponse>;
40+
};
41+
};
42+
43+
export const OpenAPI: OpenAPIConfig = {
44+
BASE: '',
45+
CREDENTIALS: 'include',
46+
ENCODE_PATH: undefined,
47+
HEADERS: undefined,
48+
PASSWORD: undefined,
49+
TOKEN: undefined,
50+
USERNAME: undefined,
51+
VERSION: '0.1.0',
52+
WITH_CREDENTIALS: false,
53+
interceptors: {
54+
request: new Interceptors(),
55+
response: new Interceptors(),
56+
},
57+
};

0 commit comments

Comments
 (0)