11import { Configuration } from './Configuration';
2- import { HttpClient, HttpOptions, HttpResponse, HttpResult } from './httpClient';
32import { Multipart, RequestFile, FormParamsType } from './multipart';
43
54export * from './models';
@@ -9,6 +8,243 @@ import { {{#models}} {{#model}}{{classname}},{{/model}} {{/models}} } from './mo
98import { {{#apiInfo} }{ {#apis} }{ {#operations} }{ {#operation} } { {operationIdCamelCase} }RequestWrapper, { {/operation} }{ {/operations} }{ {/apis} }{ {/apiInfo} } } from './models';
109
1110
11+ type StringKeyWithStringValue = Record<string , string >;
12+
13+ type ApiRequestOptions = {
14+ uri: string;
15+ body?: any;
16+ encoding?: BufferEncoding | null;
17+ form?: StringKeyWithStringValue;
18+ headers?: StringKeyWithStringValue;
19+ json?: boolean;
20+ method?: string;
21+ qs?: StringKeyWithStringValue;
22+ } ;
23+
24+ type ApiResponse = {
25+ statusCode: number;
26+ statusMessage: string;
27+ headers: NodeJS.Dict< string | string[]> ;
28+ body: any;
29+ } ;
30+
31+ type ApiResult<T = any > = {
32+ response: ApiResponse;
33+ body: T;
34+ } ;
35+
36+ type ApiRejectType = {
37+ response: ApiResponse | null;
38+ errorResponse: ApiErrorResponse | null;
39+ error: Error;
40+ } ;
41+
42+ interface FetchHeaders {
43+ forEach(callback: (value: string, key: string) => void): void;
44+ }
45+
46+ interface FetchResponse {
47+ status: number;
48+ statusText: string;
49+ headers: FetchHeaders;
50+ ok: boolean;
51+ arrayBuffer(): Promise< ArrayBuffer> ;
52+ }
53+
54+ interface FetchRequestInit {
55+ method?: string;
56+ headers?: StringKeyWithStringValue;
57+ body?: any;
58+ }
59+
60+ type Fetcher = (input: string | URL, init?: FetchRequestInit) => Promise<FetchResponse >;
61+
62+ export class ApiClient {
63+ public requestAsync(options: ApiRequestOptions): Promise< ApiResult> {
64+ const url: URL = options.qs
65+ ? new URL(`?${new URLSearchParams(options.qs).toString()} `, options.uri)
66+ : new URL(options.uri);
67+
68+ const requestBody = this.buildRequestBody(options);
69+
70+ const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8';
71+
72+ const requestOptions: FetchRequestInit = {
73+ method: options.method || ' GET' ,
74+ headers: options.headers,
75+ } ;
76+
77+ if (requestBody) {
78+ requestOptions.body = requestBody;
79+ }
80+
81+ return this.doFetchRequest(url, requestOptions, responseEncoding);
82+ }
83+
84+ private buildRequestBody(options: ApiRequestOptions) {
85+ let requestBody = options.body;
86+ if (options.form) {
87+ // Override requestBody for form with form content
88+ requestBody = new URLSearchParams(options.form).toString();
89+ options.headers = Object.assign (
90+ {
91+ ' Content-Type' : ' application/x-www-form-urlencoded' ,
92+ } ,
93+ options.headers
94+ );
95+ }
96+ if (options.json) {
97+ // Override requestBody with JSON value
98+ requestBody = JSON.stringify(options.body);
99+ options.headers = Object.assign (
100+ {
101+ ' Content-Type' : ' application/json' ,
102+ } ,
103+ options.headers
104+ );
105+ }
106+ return requestBody;
107+ }
108+
109+ private async doFetchRequest(
110+ url: URL,
111+ requestOptions: FetchRequestInit,
112+ responseEncoding: BufferEncoding | null
113+ ): Promise<ApiResult > {
114+ const fetcher = this.getFetch();
115+ let response: FetchResponse;
116+ try {
117+ response = await fetcher(url.toString(), requestOptions);
118+ } catch (error) {
119+ return Promise.reject({
120+ response: null,
121+ error: this.normalizeFetchError(error),
122+ errorResponse: null,
123+ } );
124+ }
125+
126+ const respBody = await this.readResponseBody(response, responseEncoding);
127+ const responseHeaders = this.toHeaderDict(response.headers);
128+
129+ const httpResponse: ApiResponse = {
130+ statusCode: response.status,
131+ statusMessage: response.statusText,
132+ headers: responseHeaders,
133+ body: respBody,
134+ } ;
135+
136+ if (response.ok) {
137+ return {
138+ response: httpResponse,
139+ body: respBody,
140+ } ;
141+ }
142+
143+ const rejectObject: ApiRejectType = {
144+ response: httpResponse,
145+ error: new Error(`Error on ' ${url}' : ${response.status} ${ response.statusText} `),
146+ errorResponse: null,
147+ };
148+ let errorResponse = null;
149+ try {
150+ errorResponse = JSON.parse(respBody.toString()) as ApiErrorResponse;
151+ } catch (parseError) { }
152+
153+ if (errorResponse) {
154+ rejectObject.errorResponse = errorResponse;
155+ } else {
156+ rejectObject.error.message += `. ${respBody} `;
157+ }
158+
159+ return Promise.reject(rejectObject);
160+ }
161+
162+ private async readResponseBody(
163+ response: FetchResponse,
164+ responseEncoding: BufferEncoding | null
165+ ): Promise<string | Buffer > {
166+ const arrayBuffer = await response.arrayBuffer();
167+ const buffer = Buffer.from(arrayBuffer);
168+
169+ if (responseEncoding === null) {
170+ return buffer;
171+ }
172+
173+ return buffer.toString(responseEncoding);
174+ }
175+
176+ private toHeaderDict(headers: FetchHeaders): NodeJS.Dict<string | string[] > {
177+ const normalizedHeaders: NodeJS.Dict< string | string[]> = {} ;
178+
179+ headers.forEach((value, key) => {
180+ const existing = normalizedHeaders[key];
181+ if (existing === undefined) {
182+ normalizedHeaders[key] = value;
183+ return;
184+ }
185+
186+ if (Array.isArray(existing)) {
187+ existing.push(value);
188+ normalizedHeaders[key] = existing;
189+ return;
190+ }
191+
192+ normalizedHeaders[key] = [existing, value];
193+ });
194+
195+ return normalizedHeaders;
196+ }
197+
198+ private getFetch(): Fetcher {
199+ const fetcher = (globalThis as { fetch ?: Fetcher } ).fetch;
200+ if (!fetcher) {
201+ throw new Error(' Global fetch API is not available. Please use Node.js 18+.' );
202+ }
203+
204+ return fetcher;
205+ }
206+
207+ private normalizeFetchError(error: unknown): Error {
208+ if (error instanceof Error) {
209+ const mutableError = error as Error & { code?: string; cause?: unknown; name: string } ;
210+ let normalizedCode = mutableError.code;
211+
212+ if (!normalizedCode) {
213+ const cause = mutableError.cause;
214+ if (cause && typeof cause === ' object' && ' code' in (cause as { code?: string } )) {
215+ const code = (cause as { code?: string } ).code;
216+ if (code) {
217+ normalizedCode = String(code);
218+ }
219+ }
220+ }
221+
222+ if (!normalizedCode) {
223+ normalizedCode = mutableError.name || ' FETCH_ERROR' ;
224+ }
225+
226+ try {
227+ if (! mutableError.code) {
228+ mutableError.code = normalizedCode;
229+ }
230+ } catch (assignError) { }
231+
232+ if (mutableError.code) {
233+ return mutableError;
234+ }
235+
236+ const wrapped = new Error(mutableError.message);
237+ wrapped.name = mutableError.name;
238+ (wrapped as { code?: string } ).code = normalizedCode;
239+ return wrapped;
240+ }
241+
242+ const wrapped = new Error(String(error));
243+ (wrapped as { code?: string } ).code = 'FETCH_ERROR';
244+ return wrapped;
245+ }
246+ }
247+
12248let primitives = ['string', 'boolean', 'double', 'integer', 'long', 'float', 'number', 'any'];
13249
14250class ObjectSerializer {
@@ -184,11 +420,11 @@ export class {{classname}} {
184420 ' x-aspose-client-version' : ' {{npmVersion} }'
185421 };
186422 protected _configuration: Configuration;
187- private _client: HttpClient ;
423+ private _client: ApiClient ;
188424
189425 constructor(configuration: Configuration) {
190426 this._configuration = configuration;
191- this._client = new HttpClient ();
427+ this._client = new ApiClient ();
192428 }
193429
194430{ {#operation} }
@@ -200,7 +436,7 @@ export class {{classname}} {
200436 { {/summary} }
201437 * @param request { {operationIdCamelCase} }RequestWrapper
202438 */
203- public async { {nickname} }(request: { {operationIdCamelCase} }RequestWrapper): Promise<{ response: HttpResponse ; {{#returnType} }body: { {{returnType} }}; { {/returnType} }{ {^returnType} }body?: any; { {/returnType} } }> {
439+ public async { {nickname} }(request: { {operationIdCamelCase} }RequestWrapper): Promise<{ response: ApiResponse ; {{#returnType} }body: { {{returnType} }}; { {/returnType} }{ {^returnType} }body?: any; { {/returnType} } }> {
204440 const requestPath =
205441 this._configuration.getApiBaseUrl() +
206442 ' {{{path}}}' {{#pathParams} }.replace(
@@ -247,7 +483,7 @@ export class {{classname}} {
247483 }
248484 { {/isFile} }
249485{ {/formParams} }
250- const requestOptions: HttpOptions = {
486+ const requestOptions: ApiRequestOptions = {
251487 method: ' {{httpMethod}}' ,
252488 qs: queryParameters,
253489 headers: headerParams,
@@ -272,7 +508,7 @@ export class {{classname}} {
272508
273509 await this._configuration.authentication.applyToRequestAsync(requestOptions);
274510
275- const result: HttpResult = await this._client.requestAsync(requestOptions);
511+ const result: ApiResult = await this._client.requestAsync(requestOptions);
276512
277513 { {#returnType} }
278514 return {
0 commit comments