From 52b24b504827cb38cb5072edb2ea6bb5c92c03fc Mon Sep 17 00:00:00 2001 From: Ivan Kamkin <234-Ivan.Kamkin@users.noreply.git.saltov.dynabic.com> Date: Tue, 3 Feb 2026 14:00:30 +0500 Subject: [PATCH 1/8] Rewrite httpClient to fetch api --- README.md | 5 +- package.json | 2 +- src/httpClient.ts | 225 +++++++++++++++++++++++++++++++--------------- src/multipart.ts | 2 +- 4 files changed, 161 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 3b78df0..92a8ed9 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ This repository contains Aspose.BarCode Cloud SDK for Node.js source code. To use these SDKs, you will need Client Id and Client Secret which can be looked up at [Aspose Cloud Dashboard](https://dashboard.aspose.cloud/applications) (free registration in Aspose Cloud is required for this). +## Requirements + ++ Node.js 18 or later (native `fetch` required). + ## How to use the SDK The complete source code is available in this repository folder. You can either directly use it in your project via source code or get [nmpjs distribution](https://www.npmjs.com/package/aspose-barcode-cloud-node) (recommended). @@ -126,4 +130,3 @@ RecognizeApi | [**recognizeMultipart**](docs/index.md#recognizemultipart) | **PO ScanApi | [**scan**](docs/index.md#scan) | **GET** /barcode/scan | Scan barcode from file on server in the Internet using GET requests with parameter in query string. For scaning files from your hard drive use `scan-body` or `scan-multipart` endpoints instead. ScanApi | [**scanBase64**](docs/index.md#scanbase64) | **POST** /barcode/scan-body | Scan barcode from file in request body using POST requests with parameter in body in json or xml format. ScanApi | [**scanMultipart**](docs/index.md#scanmultipart) | **POST** /barcode/scan-multipart | Scan barcode from file in request body using POST requests with parameter in multipart form. - diff --git a/package.json b/package.json index 9228dd8..bd04f5a 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "dist" ], "engines": { - "node": ">=16" + "node": ">=18" }, "scripts": { "test": "npx jest", diff --git a/src/httpClient.ts b/src/httpClient.ts index 6f15255..51a28bc 100644 --- a/src/httpClient.ts +++ b/src/httpClient.ts @@ -1,5 +1,3 @@ -import http from 'http'; -import https from 'https'; import { ApiErrorResponse } from './models'; export interface StringKeyWithStringValue { @@ -35,6 +33,26 @@ export interface HttpRejectType { error: Error; } +interface FetchHeaders { + forEach(callback: (value: string, key: string) => void): void; +} + +interface FetchResponse { + status: number; + statusText: string; + headers: FetchHeaders; + ok: boolean; + arrayBuffer(): Promise; +} + +interface FetchRequestInit { + method?: string; + headers?: StringKeyWithStringValue; + body?: any; +} + +type Fetcher = (input: string | URL, init?: FetchRequestInit) => Promise; + export class HttpClient { public requestAsync(options: HttpOptions): Promise { const url: URL = options.qs @@ -43,14 +61,18 @@ export class HttpClient { const requestBody = this.buildRequestBody(options); - const requestOptions: http.RequestOptions = { - method: options.method, + const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; + + const requestOptions: FetchRequestInit = { + method: options.method || 'GET', headers: options.headers, }; - const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; + if (requestBody) { + requestOptions.body = requestBody; + } - return this.doHttpRequest(url, requestBody, requestOptions, responseEncoding); + return this.doFetchRequest(url, requestOptions, responseEncoding); } private buildRequestBody(options: HttpOptions) { @@ -78,78 +100,141 @@ export class HttpClient { return requestBody; } - private doHttpRequest( + private async doFetchRequest( url: URL, - requestBody: any, - requestOptions: http.RequestOptions, + requestOptions: FetchRequestInit, responseEncoding: BufferEncoding | null ): Promise { - return new Promise((resolve, reject: (result: HttpRejectType) => void) => { - function requestCallback(res: http.IncomingMessage) { - if (responseEncoding) { - // encoding = null for binary responses - res.setEncoding(responseEncoding); - } - const chunks: any[] | Uint8Array[] = []; - - res.on('data', (chunk) => { - chunks.push(chunk); - }); - - res.on('end', () => { - const respBody = responseEncoding ? chunks.join('') : Buffer.concat(chunks); - - const response: HttpResponse = { - statusCode: res.statusCode!, - statusMessage: res.statusMessage!, - headers: res.headers, - body: respBody, - }; - - if (response.statusCode >= 200 && response.statusCode <= 299) { - resolve({ - response: response, - body: respBody, - }); - } else { - var rejectObject: HttpRejectType = { - response: response, - error: new Error(`Error on '${url}': ${res.statusCode} ${res.statusMessage}`), - errorResponse: null, - }; - var errorResponse = null; - try { - errorResponse = JSON.parse(respBody.toString()) as ApiErrorResponse; - } catch (parseError) {} - - if (errorResponse) { - rejectObject.errorResponse = errorResponse; - } else { - rejectObject.error.message += `. ${respBody}`; - } - reject(rejectObject); + const fetcher = this.getFetch(); + let response: FetchResponse; + try { + response = await fetcher(url.toString(), requestOptions); + } catch (error) { + return Promise.reject({ + response: null, + error: this.normalizeFetchError(error), + errorResponse: null, + }); + } + + const respBody = await this.readResponseBody(response, responseEncoding); + const responseHeaders = this.toHeaderDict(response.headers); + + const httpResponse: HttpResponse = { + statusCode: response.status, + statusMessage: response.statusText, + headers: responseHeaders, + body: respBody, + }; + + if (response.ok) { + return { + response: httpResponse, + body: respBody, + }; + } + + const rejectObject: HttpRejectType = { + response: httpResponse, + error: new Error(`Error on '${url}': ${response.status} ${response.statusText}`), + errorResponse: null, + }; + let errorResponse = null; + try { + errorResponse = JSON.parse(respBody.toString()) as ApiErrorResponse; + } catch (parseError) {} + + if (errorResponse) { + rejectObject.errorResponse = errorResponse; + } else { + rejectObject.error.message += `. ${respBody}`; + } + + return Promise.reject(rejectObject); + } + + private async readResponseBody( + response: FetchResponse, + responseEncoding: BufferEncoding | null + ): Promise { + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + if (responseEncoding === null) { + return buffer; + } + + return buffer.toString(responseEncoding); + } + + private toHeaderDict(headers: FetchHeaders): NodeJS.Dict { + const normalizedHeaders: NodeJS.Dict = {}; + + headers.forEach((value, key) => { + const existing = normalizedHeaders[key]; + if (existing === undefined) { + normalizedHeaders[key] = value; + return; + } + + if (Array.isArray(existing)) { + existing.push(value); + normalizedHeaders[key] = existing; + return; + } + + normalizedHeaders[key] = [existing, value]; + }); + + return normalizedHeaders; + } + + private getFetch(): Fetcher { + const fetcher = (globalThis as { fetch?: Fetcher }).fetch; + if (!fetcher) { + throw new Error('Global fetch API is not available. Please use Node.js 18+.'); + } + + return fetcher; + } + + private normalizeFetchError(error: unknown): Error { + if (error instanceof Error) { + const mutableError = error as Error & { code?: string; cause?: unknown; name: string }; + let normalizedCode = mutableError.code; + + if (!normalizedCode) { + const cause = mutableError.cause; + if (cause && typeof cause === 'object' && 'code' in (cause as { code?: string })) { + const code = (cause as { code?: string }).code; + if (code) { + normalizedCode = String(code); } - }); + } } - const req = - url.protocol === 'http:' - ? http.request(url, requestOptions, requestCallback) - : https.request(url, requestOptions, requestCallback); - - req.on('error', (error) => { - reject({ - response: null, - error: error, - errorResponse: null, - }); - }); + if (!normalizedCode) { + normalizedCode = mutableError.name || 'FETCH_ERROR'; + } + + try { + if (!mutableError.code) { + mutableError.code = normalizedCode; + } + } catch (assignError) {} - if (requestBody) { - req.write(requestBody); + if (mutableError.code) { + return mutableError; } - req.end(); - }); + const wrapped = new Error(mutableError.message); + wrapped.name = mutableError.name; + (wrapped as { code?: string }).code = normalizedCode; + return wrapped; + } + + const wrapped = new Error(String(error)); + (wrapped as { code?: string }).code = 'FETCH_ERROR'; + return wrapped; } } diff --git a/src/multipart.ts b/src/multipart.ts index f06a174..95f1976 100644 --- a/src/multipart.ts +++ b/src/multipart.ts @@ -1,5 +1,5 @@ import crypto from 'crypto'; -import { StringKeyWithStringValue } from 'httpClient'; +import { StringKeyWithStringValue } from './httpClient'; export interface FormParamsType extends Array> {} From efe533171be332753e5f8bdf09b4866252f0a74d Mon Sep 17 00:00:00 2001 From: Ivan Kamkin <234-Ivan.Kamkin@users.noreply.git.saltov.dynabic.com> Date: Wed, 4 Feb 2026 12:12:03 +0500 Subject: [PATCH 2/8] Rewrite httpClient to ApiClient --- README.md | 3 +- package-lock.json | 2 +- src/Authentication.ts | 7 +- src/JWTAuth.ts | 173 +++++++++- src/api.ts | 304 ++++++++++++++++-- src/httpClient.ts | 241 +------------- src/multipart.ts | 3 +- .../{httpClient.test.ts => apiClient.test.ts} | 6 +- test/errorResponse.test.ts | 5 +- 9 files changed, 444 insertions(+), 300 deletions(-) rename test/{httpClient.test.ts => apiClient.test.ts} (91%) diff --git a/README.md b/README.md index 92a8ed9..c189ef6 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ To use these SDKs, you will need Client Id and Client Secret which can be looked ## Requirements -+ Node.js 18 or later (native `fetch` required). +- Node.js 18 or later (native `fetch` required). ## How to use the SDK @@ -130,3 +130,4 @@ RecognizeApi | [**recognizeMultipart**](docs/index.md#recognizemultipart) | **PO ScanApi | [**scan**](docs/index.md#scan) | **GET** /barcode/scan | Scan barcode from file on server in the Internet using GET requests with parameter in query string. For scaning files from your hard drive use `scan-body` or `scan-multipart` endpoints instead. ScanApi | [**scanBase64**](docs/index.md#scanbase64) | **POST** /barcode/scan-body | Scan barcode from file in request body using POST requests with parameter in body in json or xml format. ScanApi | [**scanMultipart**](docs/index.md#scanmultipart) | **POST** /barcode/scan-multipart | Scan barcode from file in request body using POST requests with parameter in multipart form. + diff --git a/package-lock.json b/package-lock.json index 8cdad2d..925412b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "uuid": "^13.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@babel/code-frame": { diff --git a/src/Authentication.ts b/src/Authentication.ts index 050d5ee..a95e389 100644 --- a/src/Authentication.ts +++ b/src/Authentication.ts @@ -1,8 +1,9 @@ -import { HttpOptions } from './httpClient'; - export interface Authentication { /** * Apply authentication settings to header and query params. */ - applyToRequestAsync(requestOptions: HttpOptions): Promise; + applyToRequestAsync(requestOptions: { + headers?: Record; + qs?: Record; + }): Promise; } diff --git a/src/JWTAuth.ts b/src/JWTAuth.ts index 2dda176..c92f7d0 100644 --- a/src/JWTAuth.ts +++ b/src/JWTAuth.ts @@ -1,11 +1,42 @@ import { Configuration } from './Configuration'; import { Authentication } from './Authentication'; -import { HttpClient, HttpOptions } from './httpClient'; +import { ApiErrorResponse } from './models'; + +type StringKeyWithStringValue = Record; + +interface FetchHeaders { + forEach(callback: (value: string, key: string) => void): void; +} + +interface FetchResponse { + status: number; + statusText: string; + headers: FetchHeaders; + ok: boolean; + text(): Promise; +} + +type Fetcher = ( + input: string | URL, + init?: { method?: string; headers?: StringKeyWithStringValue; body?: any } +) => Promise; + +type AuthResponse = { + statusCode: number; + statusMessage: string; + headers: NodeJS.Dict; + body: any; +}; + +type AuthRejectType = { + response: AuthResponse | null; + errorResponse: ApiErrorResponse | null; + error: Error; +}; export class JWTAuth implements Authentication { private _accessToken?: string; private readonly _configuration: Configuration; - private _client: HttpClient; constructor(configuration: Configuration) { this._configuration = configuration; @@ -14,13 +45,15 @@ export class JWTAuth implements Authentication { // Use saved token this._accessToken = configuration.accessToken; } - this._client = new HttpClient(); } /** * Apply authentication settings to header and query params. */ - public async applyToRequestAsync(requestOptions: HttpOptions): Promise { + public async applyToRequestAsync(requestOptions: { + headers?: Record; + qs?: Record; + }): Promise { if (this._accessToken == null) { this._accessToken = await this.requestToken(); } @@ -36,18 +69,130 @@ export class JWTAuth implements Authentication { if (!this._configuration.clientId || !this._configuration.clientSecret) { throw new Error("Required 'clientId' or 'clientSecret' not specified in configuration."); } - const requestOptions: HttpOptions = { - method: 'POST', - uri: this._configuration.tokenUrl, - form: { - grant_type: 'client_credentials', - client_id: this._configuration.clientId, - client_secret: this._configuration.clientSecret, - }, + const fetcher = this.getFetch(); + const requestBody = new URLSearchParams({ + grant_type: 'client_credentials', + client_id: this._configuration.clientId, + client_secret: this._configuration.clientSecret, + }).toString(); + let response: FetchResponse; + try { + response = await fetcher(this._configuration.tokenUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: requestBody, + }); + } catch (error) { + throw { + response: null, + error: this.normalizeFetchError(error), + errorResponse: null, + } as AuthRejectType; + } + + const responseBody = await response.text(); + const responseInfo: AuthResponse = { + statusCode: response.status, + statusMessage: response.statusText, + headers: this.toHeaderDict(response.headers), + body: responseBody, }; - const result = await this._client.requestAsync(requestOptions); - const parsed = JSON.parse(result.body); + if (!response.ok) { + const rejectObject: AuthRejectType = { + response: responseInfo, + error: new Error( + `Error on '${this._configuration.tokenUrl}': ${response.status} ${response.statusText}` + ), + errorResponse: null, + }; + let errorResponse = null; + try { + errorResponse = JSON.parse(responseBody) as ApiErrorResponse; + } catch (parseError) {} + + if (errorResponse) { + rejectObject.errorResponse = errorResponse; + } else { + rejectObject.error.message += `. ${responseBody}`; + } + throw rejectObject; + } + + const parsed = JSON.parse(responseBody); return parsed.access_token; } + + private toHeaderDict(headers: FetchHeaders): NodeJS.Dict { + const normalizedHeaders: NodeJS.Dict = {}; + + headers.forEach((value, key) => { + const existing = normalizedHeaders[key]; + if (existing === undefined) { + normalizedHeaders[key] = value; + return; + } + + if (Array.isArray(existing)) { + existing.push(value); + normalizedHeaders[key] = existing; + return; + } + + normalizedHeaders[key] = [existing, value]; + }); + + return normalizedHeaders; + } + + private getFetch(): Fetcher { + const fetcher = (globalThis as { fetch?: Fetcher }).fetch; + if (!fetcher) { + throw new Error('Global fetch API is not available. Please use Node.js 18+.'); + } + + return fetcher; + } + + private normalizeFetchError(error: unknown): Error { + if (error instanceof Error) { + const mutableError = error as Error & { code?: string; cause?: unknown; name: string }; + let normalizedCode = mutableError.code; + + if (!normalizedCode) { + const cause = mutableError.cause; + if (cause && typeof cause === 'object' && 'code' in (cause as { code?: string })) { + const code = (cause as { code?: string }).code; + if (code) { + normalizedCode = String(code); + } + } + } + + if (!normalizedCode) { + normalizedCode = mutableError.name || 'FETCH_ERROR'; + } + + try { + if (!mutableError.code) { + mutableError.code = normalizedCode; + } + } catch (assignError) {} + + if (mutableError.code) { + return mutableError; + } + + const wrapped = new Error(mutableError.message); + wrapped.name = mutableError.name; + (wrapped as { code?: string }).code = normalizedCode; + return wrapped; + } + + const wrapped = new Error(String(error)); + (wrapped as { code?: string }).code = 'FETCH_ERROR'; + return wrapped; + } } diff --git a/src/api.ts b/src/api.ts index c78e932..550e1c7 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,5 +1,4 @@ import { Configuration } from './Configuration'; -import { HttpClient, HttpOptions, HttpResponse, HttpResult } from './httpClient'; import { Multipart, RequestFile, FormParamsType } from './multipart'; export * from './models'; @@ -37,6 +36,243 @@ import { ScanMultipartRequestWrapper, } from './models'; +type StringKeyWithStringValue = Record; + +type ApiRequestOptions = { + uri: string; + body?: any; + encoding?: BufferEncoding | null; + form?: StringKeyWithStringValue; + headers?: StringKeyWithStringValue; + json?: boolean; + method?: string; + qs?: StringKeyWithStringValue; +}; + +type ApiResponse = { + statusCode: number; + statusMessage: string; + headers: NodeJS.Dict; + body: any; +}; + +type ApiResult = { + response: ApiResponse; + body: T; +}; + +type ApiRejectType = { + response: ApiResponse | null; + errorResponse: ApiErrorResponse | null; + error: Error; +}; + +interface FetchHeaders { + forEach(callback: (value: string, key: string) => void): void; +} + +interface FetchResponse { + status: number; + statusText: string; + headers: FetchHeaders; + ok: boolean; + arrayBuffer(): Promise; +} + +interface FetchRequestInit { + method?: string; + headers?: StringKeyWithStringValue; + body?: any; +} + +type Fetcher = (input: string | URL, init?: FetchRequestInit) => Promise; + +export class ApiClient { + public requestAsync(options: ApiRequestOptions): Promise { + const url: URL = options.qs + ? new URL(`?${new URLSearchParams(options.qs).toString()}`, options.uri) + : new URL(options.uri); + + const requestBody = this.buildRequestBody(options); + + const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; + + const requestOptions: FetchRequestInit = { + method: options.method || 'GET', + headers: options.headers, + }; + + if (requestBody) { + requestOptions.body = requestBody; + } + + return this.doFetchRequest(url, requestOptions, responseEncoding); + } + + private buildRequestBody(options: ApiRequestOptions) { + let requestBody = options.body; + if (options.form) { + // Override requestBody for form with form content + requestBody = new URLSearchParams(options.form).toString(); + options.headers = Object.assign( + { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + options.headers + ); + } + if (options.json) { + // Override requestBody with JSON value + requestBody = JSON.stringify(options.body); + options.headers = Object.assign( + { + 'Content-Type': 'application/json', + }, + options.headers + ); + } + return requestBody; + } + + private async doFetchRequest( + url: URL, + requestOptions: FetchRequestInit, + responseEncoding: BufferEncoding | null + ): Promise { + const fetcher = this.getFetch(); + let response: FetchResponse; + try { + response = await fetcher(url.toString(), requestOptions); + } catch (error) { + return Promise.reject({ + response: null, + error: this.normalizeFetchError(error), + errorResponse: null, + }); + } + + const respBody = await this.readResponseBody(response, responseEncoding); + const responseHeaders = this.toHeaderDict(response.headers); + + const httpResponse: ApiResponse = { + statusCode: response.status, + statusMessage: response.statusText, + headers: responseHeaders, + body: respBody, + }; + + if (response.ok) { + return { + response: httpResponse, + body: respBody, + }; + } + + const rejectObject: ApiRejectType = { + response: httpResponse, + error: new Error(`Error on '${url}': ${response.status} ${response.statusText}`), + errorResponse: null, + }; + let errorResponse = null; + try { + errorResponse = JSON.parse(respBody.toString()) as ApiErrorResponse; + } catch (parseError) {} + + if (errorResponse) { + rejectObject.errorResponse = errorResponse; + } else { + rejectObject.error.message += `. ${respBody}`; + } + + return Promise.reject(rejectObject); + } + + private async readResponseBody( + response: FetchResponse, + responseEncoding: BufferEncoding | null + ): Promise { + const arrayBuffer = await response.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + + if (responseEncoding === null) { + return buffer; + } + + return buffer.toString(responseEncoding); + } + + private toHeaderDict(headers: FetchHeaders): NodeJS.Dict { + const normalizedHeaders: NodeJS.Dict = {}; + + headers.forEach((value, key) => { + const existing = normalizedHeaders[key]; + if (existing === undefined) { + normalizedHeaders[key] = value; + return; + } + + if (Array.isArray(existing)) { + existing.push(value); + normalizedHeaders[key] = existing; + return; + } + + normalizedHeaders[key] = [existing, value]; + }); + + return normalizedHeaders; + } + + private getFetch(): Fetcher { + const fetcher = (globalThis as { fetch?: Fetcher }).fetch; + if (!fetcher) { + throw new Error('Global fetch API is not available. Please use Node.js 18+.'); + } + + return fetcher; + } + + private normalizeFetchError(error: unknown): Error { + if (error instanceof Error) { + const mutableError = error as Error & { code?: string; cause?: unknown; name: string }; + let normalizedCode = mutableError.code; + + if (!normalizedCode) { + const cause = mutableError.cause; + if (cause && typeof cause === 'object' && 'code' in (cause as { code?: string })) { + const code = (cause as { code?: string }).code; + if (code) { + normalizedCode = String(code); + } + } + } + + if (!normalizedCode) { + normalizedCode = mutableError.name || 'FETCH_ERROR'; + } + + try { + if (!mutableError.code) { + mutableError.code = normalizedCode; + } + } catch (assignError) {} + + if (mutableError.code) { + return mutableError; + } + + const wrapped = new Error(mutableError.message); + wrapped.name = mutableError.name; + (wrapped as { code?: string }).code = normalizedCode; + return wrapped; + } + + const wrapped = new Error(String(error)); + (wrapped as { code?: string }).code = 'FETCH_ERROR'; + return wrapped; + } +} + let primitives = ['string', 'boolean', 'double', 'integer', 'long', 'float', 'number', 'any']; class ObjectSerializer { @@ -206,11 +442,11 @@ export class GenerateApi { 'x-aspose-client-version': '26.1.0', }; protected _configuration: Configuration; - private _client: HttpClient; + private _client: ApiClient; constructor(configuration: Configuration) { this._configuration = configuration; - this._client = new HttpClient(); + this._client = new ApiClient(); } /** @@ -218,7 +454,7 @@ export class GenerateApi { * @summary Generate barcode using GET request with parameters in route and query string. * @param request GenerateRequestWrapper */ - public async generate(request: GenerateRequestWrapper): Promise<{ response: HttpResponse; body: Buffer }> { + public async generate(request: GenerateRequestWrapper): Promise<{ response: ApiResponse; body: Buffer }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/generate/{barcodeType}'.replace('{' + 'barcodeType' + '}', String(request.barcodeType)); @@ -279,7 +515,7 @@ export class GenerateApi { queryParameters['rotationAngle'] = ObjectSerializer.serialize(request.rotationAngle, 'number'); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'GET', qs: queryParameters, headers: headerParams, @@ -289,7 +525,7 @@ export class GenerateApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -302,7 +538,7 @@ export class GenerateApi { * @summary Generate barcode using POST request with parameters in body in json or xml format. * @param request GenerateBodyRequestWrapper */ - public async generateBody(request: GenerateBodyRequestWrapper): Promise<{ response: HttpResponse; body: Buffer }> { + public async generateBody(request: GenerateBodyRequestWrapper): Promise<{ response: ApiResponse; body: Buffer }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/generate-body'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -314,7 +550,7 @@ export class GenerateApi { ); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'POST', qs: queryParameters, headers: headerParams, @@ -326,7 +562,7 @@ export class GenerateApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -341,7 +577,7 @@ export class GenerateApi { */ public async generateMultipart( request: GenerateMultipartRequestWrapper - ): Promise<{ response: HttpResponse; body: Buffer }> { + ): Promise<{ response: ApiResponse; body: Buffer }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/generate-multipart'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -395,7 +631,7 @@ export class GenerateApi { if (request.rotationAngle != null) { formParams.push(['rotationAngle', ObjectSerializer.serialize(request.rotationAngle, 'number')]); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'POST', qs: queryParameters, headers: headerParams, @@ -411,7 +647,7 @@ export class GenerateApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -426,11 +662,11 @@ export class RecognizeApi { 'x-aspose-client-version': '26.1.0', }; protected _configuration: Configuration; - private _client: HttpClient; + private _client: ApiClient; constructor(configuration: Configuration) { this._configuration = configuration; - this._client = new HttpClient(); + this._client = new ApiClient(); } /** @@ -440,7 +676,7 @@ export class RecognizeApi { */ public async recognize( request: RecognizeRequestWrapper - ): Promise<{ response: HttpResponse; body: BarcodeResponseList }> { + ): Promise<{ response: ApiResponse; body: BarcodeResponseList }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/recognize'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -474,7 +710,7 @@ export class RecognizeApi { ); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'GET', qs: queryParameters, headers: headerParams, @@ -483,7 +719,7 @@ export class RecognizeApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -498,7 +734,7 @@ export class RecognizeApi { */ public async recognizeBase64( request: RecognizeBase64RequestWrapper - ): Promise<{ response: HttpResponse; body: BarcodeResponseList }> { + ): Promise<{ response: ApiResponse; body: BarcodeResponseList }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/recognize-body'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -510,7 +746,7 @@ export class RecognizeApi { ); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'POST', qs: queryParameters, headers: headerParams, @@ -521,7 +757,7 @@ export class RecognizeApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -536,7 +772,7 @@ export class RecognizeApi { */ public async recognizeMultipart( request: RecognizeMultipartRequestWrapper - ): Promise<{ response: HttpResponse; body: BarcodeResponseList }> { + ): Promise<{ response: ApiResponse; body: BarcodeResponseList }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/recognize-multipart'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -571,7 +807,7 @@ export class RecognizeApi { ObjectSerializer.serialize(request.recognitionImageKind, 'RecognitionImageKind'), ]); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'POST', qs: queryParameters, headers: headerParams, @@ -586,7 +822,7 @@ export class RecognizeApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -601,11 +837,11 @@ export class ScanApi { 'x-aspose-client-version': '26.1.0', }; protected _configuration: Configuration; - private _client: HttpClient; + private _client: ApiClient; constructor(configuration: Configuration) { this._configuration = configuration; - this._client = new HttpClient(); + this._client = new ApiClient(); } /** @@ -613,7 +849,7 @@ export class ScanApi { * @summary Scan barcode from file on server in the Internet using GET requests with parameter in query string. For scaning files from your hard drive use `scan-body` or `scan-multipart` endpoints instead. * @param request ScanRequestWrapper */ - public async scan(request: ScanRequestWrapper): Promise<{ response: HttpResponse; body: BarcodeResponseList }> { + public async scan(request: ScanRequestWrapper): Promise<{ response: ApiResponse; body: BarcodeResponseList }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/scan'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -627,7 +863,7 @@ export class ScanApi { queryParameters['fileUrl'] = ObjectSerializer.serialize(request.fileUrl, 'string'); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'GET', qs: queryParameters, headers: headerParams, @@ -636,7 +872,7 @@ export class ScanApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -651,7 +887,7 @@ export class ScanApi { */ public async scanBase64( request: ScanBase64RequestWrapper - ): Promise<{ response: HttpResponse; body: BarcodeResponseList }> { + ): Promise<{ response: ApiResponse; body: BarcodeResponseList }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/scan-body'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -663,7 +899,7 @@ export class ScanApi { ); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'POST', qs: queryParameters, headers: headerParams, @@ -674,7 +910,7 @@ export class ScanApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, @@ -689,7 +925,7 @@ export class ScanApi { */ public async scanMultipart( request: ScanMultipartRequestWrapper - ): Promise<{ response: HttpResponse; body: BarcodeResponseList }> { + ): Promise<{ response: ApiResponse; body: BarcodeResponseList }> { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/scan-multipart'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); @@ -700,7 +936,7 @@ export class ScanApi { throw new Error('Required parameter request.fileBytes was null or undefined when calling scanMultipart.'); } - const requestOptions: HttpOptions = { + const requestOptions: ApiRequestOptions = { method: 'POST', qs: queryParameters, headers: headerParams, @@ -715,7 +951,7 @@ export class ScanApi { await this._configuration.authentication.applyToRequestAsync(requestOptions); - const result: HttpResult = await this._client.requestAsync(requestOptions); + const result: ApiResult = await this._client.requestAsync(requestOptions); return { response: result.response, diff --git a/src/httpClient.ts b/src/httpClient.ts index 51a28bc..cb0ff5c 100644 --- a/src/httpClient.ts +++ b/src/httpClient.ts @@ -1,240 +1 @@ -import { ApiErrorResponse } from './models'; - -export interface StringKeyWithStringValue { - [key: string]: string; -} - -export interface HttpOptions { - uri: string; - body?: any; - encoding?: BufferEncoding | null; - form?: StringKeyWithStringValue; - headers?: StringKeyWithStringValue; - json?: boolean; - method?: string; - qs?: StringKeyWithStringValue; -} - -export interface HttpResponse { - statusCode: number; - statusMessage: string; - headers: NodeJS.Dict; - body: any; -} - -export interface HttpResult { - response: HttpResponse; - body: any; -} - -export interface HttpRejectType { - response: HttpResponse | null; - errorResponse: ApiErrorResponse | null; - error: Error; -} - -interface FetchHeaders { - forEach(callback: (value: string, key: string) => void): void; -} - -interface FetchResponse { - status: number; - statusText: string; - headers: FetchHeaders; - ok: boolean; - arrayBuffer(): Promise; -} - -interface FetchRequestInit { - method?: string; - headers?: StringKeyWithStringValue; - body?: any; -} - -type Fetcher = (input: string | URL, init?: FetchRequestInit) => Promise; - -export class HttpClient { - public requestAsync(options: HttpOptions): Promise { - const url: URL = options.qs - ? new URL(`?${new URLSearchParams(options.qs).toString()}`, options.uri) - : new URL(options.uri); - - const requestBody = this.buildRequestBody(options); - - const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; - - const requestOptions: FetchRequestInit = { - method: options.method || 'GET', - headers: options.headers, - }; - - if (requestBody) { - requestOptions.body = requestBody; - } - - return this.doFetchRequest(url, requestOptions, responseEncoding); - } - - private buildRequestBody(options: HttpOptions) { - let requestBody = options.body; - if (options.form) { - // Override requestBody for form with form content - requestBody = new URLSearchParams(options.form).toString(); - options.headers = Object.assign( - { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - options.headers - ); - } - if (options.json) { - // Override requestBody with JSON value - requestBody = JSON.stringify(options.body); - options.headers = Object.assign( - { - 'Content-Type': 'application/json', - }, - options.headers - ); - } - return requestBody; - } - - private async doFetchRequest( - url: URL, - requestOptions: FetchRequestInit, - responseEncoding: BufferEncoding | null - ): Promise { - const fetcher = this.getFetch(); - let response: FetchResponse; - try { - response = await fetcher(url.toString(), requestOptions); - } catch (error) { - return Promise.reject({ - response: null, - error: this.normalizeFetchError(error), - errorResponse: null, - }); - } - - const respBody = await this.readResponseBody(response, responseEncoding); - const responseHeaders = this.toHeaderDict(response.headers); - - const httpResponse: HttpResponse = { - statusCode: response.status, - statusMessage: response.statusText, - headers: responseHeaders, - body: respBody, - }; - - if (response.ok) { - return { - response: httpResponse, - body: respBody, - }; - } - - const rejectObject: HttpRejectType = { - response: httpResponse, - error: new Error(`Error on '${url}': ${response.status} ${response.statusText}`), - errorResponse: null, - }; - let errorResponse = null; - try { - errorResponse = JSON.parse(respBody.toString()) as ApiErrorResponse; - } catch (parseError) {} - - if (errorResponse) { - rejectObject.errorResponse = errorResponse; - } else { - rejectObject.error.message += `. ${respBody}`; - } - - return Promise.reject(rejectObject); - } - - private async readResponseBody( - response: FetchResponse, - responseEncoding: BufferEncoding | null - ): Promise { - const arrayBuffer = await response.arrayBuffer(); - const buffer = Buffer.from(arrayBuffer); - - if (responseEncoding === null) { - return buffer; - } - - return buffer.toString(responseEncoding); - } - - private toHeaderDict(headers: FetchHeaders): NodeJS.Dict { - const normalizedHeaders: NodeJS.Dict = {}; - - headers.forEach((value, key) => { - const existing = normalizedHeaders[key]; - if (existing === undefined) { - normalizedHeaders[key] = value; - return; - } - - if (Array.isArray(existing)) { - existing.push(value); - normalizedHeaders[key] = existing; - return; - } - - normalizedHeaders[key] = [existing, value]; - }); - - return normalizedHeaders; - } - - private getFetch(): Fetcher { - const fetcher = (globalThis as { fetch?: Fetcher }).fetch; - if (!fetcher) { - throw new Error('Global fetch API is not available. Please use Node.js 18+.'); - } - - return fetcher; - } - - private normalizeFetchError(error: unknown): Error { - if (error instanceof Error) { - const mutableError = error as Error & { code?: string; cause?: unknown; name: string }; - let normalizedCode = mutableError.code; - - if (!normalizedCode) { - const cause = mutableError.cause; - if (cause && typeof cause === 'object' && 'code' in (cause as { code?: string })) { - const code = (cause as { code?: string }).code; - if (code) { - normalizedCode = String(code); - } - } - } - - if (!normalizedCode) { - normalizedCode = mutableError.name || 'FETCH_ERROR'; - } - - try { - if (!mutableError.code) { - mutableError.code = normalizedCode; - } - } catch (assignError) {} - - if (mutableError.code) { - return mutableError; - } - - const wrapped = new Error(mutableError.message); - wrapped.name = mutableError.name; - (wrapped as { code?: string }).code = normalizedCode; - return wrapped; - } - - const wrapped = new Error(String(error)); - (wrapped as { code?: string }).code = 'FETCH_ERROR'; - return wrapped; - } -} +export {}; diff --git a/src/multipart.ts b/src/multipart.ts index 95f1976..9342b1a 100644 --- a/src/multipart.ts +++ b/src/multipart.ts @@ -1,5 +1,6 @@ import crypto from 'crypto'; -import { StringKeyWithStringValue } from './httpClient'; + +type StringKeyWithStringValue = Record; export interface FormParamsType extends Array> {} diff --git a/test/httpClient.test.ts b/test/apiClient.test.ts similarity index 91% rename from test/httpClient.test.ts rename to test/apiClient.test.ts index bade9fc..3ab5c6d 100644 --- a/test/httpClient.test.ts +++ b/test/apiClient.test.ts @@ -1,11 +1,11 @@ import assert from 'assert'; -import { HttpClient } from '../src/httpClient'; +import { ApiClient } from '../src/api'; -describe('httpClient tests', () => { +describe('api client tests', () => { jest.setTimeout(60000); - const client = new HttpClient(); + const client = new ApiClient(); it('should return response', async () => { const response = await client.requestAsync({ diff --git a/test/errorResponse.test.ts b/test/errorResponse.test.ts index 72d5096..61e5ddc 100644 --- a/test/errorResponse.test.ts +++ b/test/errorResponse.test.ts @@ -1,7 +1,6 @@ import assert from 'assert'; import * as Barcode from '../src/api'; -import { HttpRejectType } from '../src/httpClient'; import { LoadTestConfiguration } from './helpers'; describe('Error response tests', () => { @@ -14,10 +13,10 @@ describe('Error response tests', () => { try { await api.generate(request); - assert.fail('Expected HttpRejectType was not thrown'); + assert.fail('Expected API error was not thrown'); } catch (err) { assert.ok(err); - var typedError = err as HttpRejectType; + const typedError = err as { errorResponse?: { error?: { message?: string } } }; assert.ok(typedError); assert.ok(typedError.errorResponse?.error); assert.strictEqual( From 058738586d28e935ab83989034149ac3691f5fa6 Mon Sep 17 00:00:00 2001 From: Ivan Kamkin <234-Ivan.Kamkin@users.noreply.git.saltov.dynabic.com> Date: Wed, 4 Feb 2026 12:16:39 +0500 Subject: [PATCH 3/8] Remove httpClient --- src/httpClient.ts | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/httpClient.ts diff --git a/src/httpClient.ts b/src/httpClient.ts deleted file mode 100644 index cb0ff5c..0000000 --- a/src/httpClient.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; From 5fb2e2335870dc09446eba19a78bbcac56a72fc2 Mon Sep 17 00:00:00 2001 From: Denis Averin Date: Wed, 4 Feb 2026 19:31:35 +0700 Subject: [PATCH 4/8] Rename FetchRequestInit to FetchRequestOptions --- src/api.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api.ts b/src/api.ts index 550e1c7..a2d2926 100644 --- a/src/api.ts +++ b/src/api.ts @@ -79,13 +79,13 @@ interface FetchResponse { arrayBuffer(): Promise; } -interface FetchRequestInit { +interface FetchRequestOptions { method?: string; headers?: StringKeyWithStringValue; body?: any; } -type Fetcher = (input: string | URL, init?: FetchRequestInit) => Promise; +type Fetcher = (input: string | URL, options?: FetchRequestOptions) => Promise; export class ApiClient { public requestAsync(options: ApiRequestOptions): Promise { @@ -97,7 +97,7 @@ export class ApiClient { const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; - const requestOptions: FetchRequestInit = { + const requestOptions: FetchRequestOptions = { method: options.method || 'GET', headers: options.headers, }; @@ -136,7 +136,7 @@ export class ApiClient { private async doFetchRequest( url: URL, - requestOptions: FetchRequestInit, + requestOptions: FetchRequestOptions, responseEncoding: BufferEncoding | null ): Promise { const fetcher = this.getFetch(); From afa8af3972c6c4fb109479db71d0c9d82fb1e416 Mon Sep 17 00:00:00 2001 From: Denis Averin Date: Wed, 4 Feb 2026 19:35:37 +0700 Subject: [PATCH 5/8] Renames applied in src/api.ts, src/multipart.ts, and src/JWTAuth.ts --- src/JWTAuth.ts | 4 ++-- src/api.ts | 26 +++++++++++++------------- src/multipart.ts | 8 ++++---- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/JWTAuth.ts b/src/JWTAuth.ts index c92f7d0..242dae6 100644 --- a/src/JWTAuth.ts +++ b/src/JWTAuth.ts @@ -2,7 +2,7 @@ import { Configuration } from './Configuration'; import { Authentication } from './Authentication'; import { ApiErrorResponse } from './models'; -type StringKeyWithStringValue = Record; +type StringMap = Record; interface FetchHeaders { forEach(callback: (value: string, key: string) => void): void; @@ -18,7 +18,7 @@ interface FetchResponse { type Fetcher = ( input: string | URL, - init?: { method?: string; headers?: StringKeyWithStringValue; body?: any } + init?: { method?: string; headers?: StringMap; body?: any } ) => Promise; type AuthResponse = { diff --git a/src/api.ts b/src/api.ts index a2d2926..c1c751f 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,5 +1,5 @@ import { Configuration } from './Configuration'; -import { Multipart, RequestFile, FormParamsType } from './multipart'; +import { Multipart, RequestFile, FormParamPairs } from './multipart'; export * from './models'; @@ -36,17 +36,17 @@ import { ScanMultipartRequestWrapper, } from './models'; -type StringKeyWithStringValue = Record; +type StringMap = Record; type ApiRequestOptions = { uri: string; body?: any; encoding?: BufferEncoding | null; - form?: StringKeyWithStringValue; - headers?: StringKeyWithStringValue; + form?: StringMap; + headers?: StringMap; json?: boolean; method?: string; - qs?: StringKeyWithStringValue; + qs?: StringMap; }; type ApiResponse = { @@ -79,13 +79,13 @@ interface FetchResponse { arrayBuffer(): Promise; } -interface FetchRequestOptions { +interface FetchOptions { method?: string; - headers?: StringKeyWithStringValue; + headers?: StringMap; body?: any; } -type Fetcher = (input: string | URL, options?: FetchRequestOptions) => Promise; +type Fetcher = (input: string | URL, options?: FetchOptions) => Promise; export class ApiClient { public requestAsync(options: ApiRequestOptions): Promise { @@ -97,7 +97,7 @@ export class ApiClient { const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; - const requestOptions: FetchRequestOptions = { + const requestOptions: FetchOptions = { method: options.method || 'GET', headers: options.headers, }; @@ -136,7 +136,7 @@ export class ApiClient { private async doFetchRequest( url: URL, - requestOptions: FetchRequestOptions, + requestOptions: FetchOptions, responseEncoding: BufferEncoding | null ): Promise { const fetcher = this.getFetch(); @@ -581,7 +581,7 @@ export class GenerateApi { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/generate-multipart'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); - const formParams: FormParamsType = []; + const formParams: FormParamPairs = []; // verify required parameter 'request.barcodeType' is not null or undefined if (request.barcodeType == null) { @@ -776,7 +776,7 @@ export class RecognizeApi { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/recognize-multipart'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); - const formParams: FormParamsType = []; + const formParams: FormParamPairs = []; // verify required parameter 'request.barcodeType' is not null or undefined if (request.barcodeType == null) { @@ -929,7 +929,7 @@ export class ScanApi { const requestPath = this._configuration.getApiBaseUrl() + '/barcode/scan-multipart'; let queryParameters: any = {}; let headerParams: any = (Object as any).assign({}, this.defaultHeaders); - const formParams: FormParamsType = []; + const formParams: FormParamPairs = []; // verify required parameter 'request.fileBytes' is not null or undefined if (request.fileBytes == null) { diff --git a/src/multipart.ts b/src/multipart.ts index 9342b1a..5a4aaa3 100644 --- a/src/multipart.ts +++ b/src/multipart.ts @@ -1,8 +1,8 @@ import crypto from 'crypto'; -type StringKeyWithStringValue = Record; +type StringMap = Record; -export interface FormParamsType extends Array> {} +export interface FormParamPairs extends Array> {} interface IRequestFile { name: string; @@ -23,9 +23,9 @@ export class RequestFile implements IRequestFile { export class Multipart { readonly boundary: string; readonly body: Buffer; - readonly headers: StringKeyWithStringValue; + readonly headers: StringMap; - constructor(textFields: FormParamsType, files?: IRequestFile[]) { + constructor(textFields: FormParamPairs, files?: IRequestFile[]) { const random = crypto.randomUUID(); this.boundary = '------------------------' + random.replace(/-/g, ''); From b6c32c964c5718295eab7d0f4768a714a71794e8 Mon Sep 17 00:00:00 2001 From: Denis Averin Date: Wed, 4 Feb 2026 21:28:42 +0700 Subject: [PATCH 6/8] Rename test files --- scripts/run_example.sh | 5 +++++ scripts/run_snippet.sh | 4 ++-- scripts/run_snippets.sh | 2 +- snippets/generate/appearance/generateBody.js | 2 +- .../set-colorscheme/generateMultipart.js | 2 +- testdata/{Code39.png => Code39-green.png} | Bin testdata/Code39-red.png | Bin 0 -> 3694 bytes 7 files changed, 10 insertions(+), 5 deletions(-) mode change 100644 => 100755 scripts/run_snippet.sh mode change 100644 => 100755 scripts/run_snippets.sh rename testdata/{Code39.png => Code39-green.png} (100%) create mode 100644 testdata/Code39-red.png diff --git a/scripts/run_example.sh b/scripts/run_example.sh index f28d1b2..e6825a4 100755 --- a/scripts/run_example.sh +++ b/scripts/run_example.sh @@ -2,6 +2,11 @@ set -euo pipefail +if [[ "${ASPOSE_E2E:-}" != "1" ]]; then + echo "Skipping example: set ASPOSE_E2E=1 to run against live API." + exit 0 +fi + TEST_DIR="demo" rm -rf "${TEST_DIR}" || true diff --git a/scripts/run_snippet.sh b/scripts/run_snippet.sh old mode 100644 new mode 100755 index 868ee56..f8dc539 --- a/scripts/run_snippet.sh +++ b/scripts/run_snippet.sh @@ -8,6 +8,6 @@ SCRIPT_DIR=$3 CONFIG_FILE_PATH=$4 echo "Run snippet file: $FILE_PATH" -node ${SCRIPT_DIR}/insert-credentials.js $FILE_PATH $CONFIG_FILE_PATH $RUN_DIR +node "${SCRIPT_DIR}/insert-credentials.js" "$FILE_PATH" "$CONFIG_FILE_PATH" "$RUN_DIR" -node ./$RUN_DIR/"${FILE_PATH##*/}" || exit 1 \ No newline at end of file +node "./$RUN_DIR/${FILE_PATH##*/}" || exit 1 diff --git a/scripts/run_snippets.sh b/scripts/run_snippets.sh old mode 100644 new mode 100755 index 7df076c..f19208f --- a/scripts/run_snippets.sh +++ b/scripts/run_snippets.sh @@ -29,4 +29,4 @@ popd ${SCRIPT_DIR}/run_snippet.sh "${SNIPPETS_DIR}/manualFetchToken.js" $RUN_DIR $SCRIPT_DIR $CONFIG_FILE_PATH || exit 1; -rm -rf "${RUN_DIR}" || true \ No newline at end of file +rm -rf "${RUN_DIR}" || true diff --git a/snippets/generate/appearance/generateBody.js b/snippets/generate/appearance/generateBody.js index 0bb22fb..2467044 100644 --- a/snippets/generate/appearance/generateBody.js +++ b/snippets/generate/appearance/generateBody.js @@ -41,7 +41,7 @@ async function generateBarcode(api, fileName) { } const genApi = new Barcode.GenerateApi(config); -const fileName = path.resolve('testdata', 'Code39.png'); +const fileName = path.resolve('testdata', 'Code39-red.png'); generateBarcode(genApi, fileName) .then(() => { diff --git a/snippets/generate/set-colorscheme/generateMultipart.js b/snippets/generate/set-colorscheme/generateMultipart.js index 9a671d5..8995b3b 100644 --- a/snippets/generate/set-colorscheme/generateMultipart.js +++ b/snippets/generate/set-colorscheme/generateMultipart.js @@ -36,7 +36,7 @@ function generateBarcode(api, fileName) { } const genApi = new Barcode.GenerateApi(config); -const fileName = path.resolve('testdata', 'Code39.png'); +const fileName = path.resolve('testdata', 'Code39-green.png'); generateBarcode(genApi, fileName) .then(() => { diff --git a/testdata/Code39.png b/testdata/Code39-green.png similarity index 100% rename from testdata/Code39.png rename to testdata/Code39-green.png diff --git a/testdata/Code39-red.png b/testdata/Code39-red.png new file mode 100644 index 0000000000000000000000000000000000000000..9c556c42aff6a7719ff1c10341182d3cbfed7bc4 GIT binary patch literal 3694 zcmds2dpMM78-K=N7;HRKKBDa?5B%s7 z&)5Qho0}<60077VO^h7CKxqwpwBE|=p=^x7&g{!Td9KW?EIr3albzZ3hw|+D4!)$C z|Gu#R9KZotxC5m3vSwwBR6g;C9RN@s#xXQDHp-q%j$oPx21jnDn+HXPlj8%U$QI^H z$iR^r9~BrBMvv9qOb=m1P>7>7T%s-`m_j7#Ql0R4$C%(P_FgMi>c)p}jbPfte-z?d zgDe)yoMmMm852UbSh{p6d5I<2(sD68v6z_<5gQo4ID)B9)ODIQv4YMFieW^>G9n}1 znp(z;ie%E=!UN%FLuQRKASX)~YNJ}YpmX79D)|OHu7)3=z zWhLeLDk}3W^|bXY|M5bv0aXPM2549e5y+@wu&NmJCLqA1ki*O@X(mV)OaWOrd7OeG zUI`wk{2a(&uvi&ctel)I%oa?NG(}`p<<#^nmdVfc48#$m)h%~tom9|Ye&M=?*Y9Hn zR-0q?DB|aRF@J%kp%Ka0#MIj6OIy38_RcF@R=TcoTm8-Y4c;3!`S=C})3=0#GQMZV z#%*K8Cu~pNo06LL!@l%`*@tot=jI*x=~TflrwfbDoGrdsUQv0e>T>m!8+A8t)i*TW zzVqN=OY5Vy_Kr?oPw(@-7yU0^4UE5@n4IFj5eP+6UKoI#r3HUK%1agUl983g%HpKF zFfuGDaaCD4JqvlYWuCae=($A8-3scuq39OgCOXe;GAmx0?sF_tW_L?=`!~gA`T9c_ zCBHUJJJr1qUhb&U9%-K6;Q!Ds({!i+1&Z_1P_XQn0}5oa#i1zJyWbfFC+;0X!EQIi z=4TYBQzh=*LV1qhBnk!sP|#@ec!{#(RvP>wb61ntf{y3cTJR%L>{bAU=}oQ`@Pxsnwdav8+(`BXK{jgMw=W>EKa#{FsBF&qjmmPM68M(>jg;05R~J+iaA`npS|3AQ)Y zt10=_Oj}_qeS4BH`kPPFUP>@-|yi$6)z+>A5fA_?N zH~o6#&D`ARc27?X=_XgiQ0#2_0((i6xx(!UI_C4#ay;UkYG_|OroP-)R5D;-ADXgI z$InN{JL~*8jeA-nF})`p(u^)m5rp#JqM(5*%0&Sue~K0>fqqF!DqnA1Q(}n-pO+{6 z$_hT?N@GQ!njVl_k#n45f9+zh#mwJ6B8?hM%eSvTQiN@Gir%%7^)F zILua>{_4ATt|pWo&#fI53|5I%K7(FR872rK!;V0AI3R^o7;6>^f?UOV-BT6<7;7wd zT2tgfcSUyYR2Gc}!HUf13eD^z#kLh}UTtXz^7*JJPfUR|*)7Q+jHR(#8esZ-2Gb{$ zznTXJn3)`qKRi1T=<=A5di0Tt2vg@e|^xj4k;Hf+!R9ZnL zo;_ZGs4Zq9$xc#b5ehV^o$MMx+H}Yp_UC^E@MnDc-v{s)d<U><<_IgBv^XvW}rnIyUrMjKiXLyHrSn}%e`2&=y zANkF-TfJAFJJ|L8ws|Fs#(h^`JfgWk)9QsvjeMW(B)MTn;zr6G&ZziEQz~LqoBwyV77<)QMLYfj;VI#N literal 0 HcmV?d00001 From 5a25ed14bc34f82953d89a6bcff4b4d6759573fb Mon Sep 17 00:00:00 2001 From: Denis Averin Date: Wed, 4 Feb 2026 21:48:25 +0700 Subject: [PATCH 7/8] Use early init for fetcher --- Makefile | 6 +++++- README.md | 2 +- package.json | 1 + src/JWTAuth.ts | 42 ++++++++++-------------------------------- src/api.ts | 49 +++++++++++++++---------------------------------- 5 files changed, 32 insertions(+), 68 deletions(-) diff --git a/Makefile b/Makefile index 5d02441..9f06519 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,11 @@ npm-test: npm test .PHONY: test -test: npm-test example snippets +test: typecheck npm-test example snippets + +.PHONY: typecheck +typecheck: + npm run typecheck .PHONY: cover cover: diff --git a/README.md b/README.md index c189ef6..feea673 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ const config = new Barcode.Configuration( async function generateBarcode(api) { const request = new Barcode.GenerateRequestWrapper( - Barcode.EncodeBarcodeType.Qr, + Barcode.EncodeBarcodeType.Qr, 'Aspose.BarCode for Cloud Sample'); const oneBarcode = await api.generate(request); diff --git a/package.json b/package.json index bd04f5a..12a128b 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,7 @@ }, "scripts": { "test": "npx jest", + "typecheck": "npx tsc --noEmit", "cover": "npx jest --coverage", "lint": "npx eslint src test snippets", "format": "npx eslint src test snippets eslint.config.mjs --fix", diff --git a/src/JWTAuth.ts b/src/JWTAuth.ts index 242dae6..e8fe7b8 100644 --- a/src/JWTAuth.ts +++ b/src/JWTAuth.ts @@ -2,25 +2,6 @@ import { Configuration } from './Configuration'; import { Authentication } from './Authentication'; import { ApiErrorResponse } from './models'; -type StringMap = Record; - -interface FetchHeaders { - forEach(callback: (value: string, key: string) => void): void; -} - -interface FetchResponse { - status: number; - statusText: string; - headers: FetchHeaders; - ok: boolean; - text(): Promise; -} - -type Fetcher = ( - input: string | URL, - init?: { method?: string; headers?: StringMap; body?: any } -) => Promise; - type AuthResponse = { statusCode: number; statusMessage: string; @@ -37,9 +18,16 @@ type AuthRejectType = { export class JWTAuth implements Authentication { private _accessToken?: string; private readonly _configuration: Configuration; + private readonly _fetcher: typeof fetch; constructor(configuration: Configuration) { this._configuration = configuration; + const resolvedFetch = (globalThis as { fetch?: typeof fetch }).fetch; + if (!resolvedFetch) { + throw new Error('Global fetch API is not available. Please use Node.js 18+.'); + } + + this._fetcher = resolvedFetch; if (configuration.accessToken) { // Use saved token @@ -69,15 +57,14 @@ export class JWTAuth implements Authentication { if (!this._configuration.clientId || !this._configuration.clientSecret) { throw new Error("Required 'clientId' or 'clientSecret' not specified in configuration."); } - const fetcher = this.getFetch(); const requestBody = new URLSearchParams({ grant_type: 'client_credentials', client_id: this._configuration.clientId, client_secret: this._configuration.clientSecret, }).toString(); - let response: FetchResponse; + let response: Response; try { - response = await fetcher(this._configuration.tokenUrl, { + response = await this._fetcher(this._configuration.tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -125,7 +112,7 @@ export class JWTAuth implements Authentication { return parsed.access_token; } - private toHeaderDict(headers: FetchHeaders): NodeJS.Dict { + private toHeaderDict(headers: Headers): NodeJS.Dict { const normalizedHeaders: NodeJS.Dict = {}; headers.forEach((value, key) => { @@ -147,15 +134,6 @@ export class JWTAuth implements Authentication { return normalizedHeaders; } - private getFetch(): Fetcher { - const fetcher = (globalThis as { fetch?: Fetcher }).fetch; - if (!fetcher) { - throw new Error('Global fetch API is not available. Please use Node.js 18+.'); - } - - return fetcher; - } - private normalizeFetchError(error: unknown): Error { if (error instanceof Error) { const mutableError = error as Error & { code?: string; cause?: unknown; name: string }; diff --git a/src/api.ts b/src/api.ts index c1c751f..346bad7 100644 --- a/src/api.ts +++ b/src/api.ts @@ -67,27 +67,18 @@ type ApiRejectType = { error: Error; }; -interface FetchHeaders { - forEach(callback: (value: string, key: string) => void): void; -} - -interface FetchResponse { - status: number; - statusText: string; - headers: FetchHeaders; - ok: boolean; - arrayBuffer(): Promise; -} +export class ApiClient { + private readonly _fetcher: typeof fetch; -interface FetchOptions { - method?: string; - headers?: StringMap; - body?: any; -} + constructor() { + const resolvedFetch = (globalThis as { fetch?: typeof fetch }).fetch; + if (!resolvedFetch) { + throw new Error('Global fetch API is not available. Please use Node.js 18+.'); + } -type Fetcher = (input: string | URL, options?: FetchOptions) => Promise; + this._fetcher = resolvedFetch; + } -export class ApiClient { public requestAsync(options: ApiRequestOptions): Promise { const url: URL = options.qs ? new URL(`?${new URLSearchParams(options.qs).toString()}`, options.uri) @@ -97,7 +88,7 @@ export class ApiClient { const responseEncoding: BufferEncoding | null = options.encoding === null ? null : options.encoding || 'utf-8'; - const requestOptions: FetchOptions = { + const requestOptions: RequestInit = { method: options.method || 'GET', headers: options.headers, }; @@ -136,13 +127,12 @@ export class ApiClient { private async doFetchRequest( url: URL, - requestOptions: FetchOptions, + requestOptions: RequestInit, responseEncoding: BufferEncoding | null ): Promise { - const fetcher = this.getFetch(); - let response: FetchResponse; + let response: Response; try { - response = await fetcher(url.toString(), requestOptions); + response = await this._fetcher(url.toString(), requestOptions); } catch (error) { return Promise.reject({ response: null, @@ -188,7 +178,7 @@ export class ApiClient { } private async readResponseBody( - response: FetchResponse, + response: Response, responseEncoding: BufferEncoding | null ): Promise { const arrayBuffer = await response.arrayBuffer(); @@ -201,7 +191,7 @@ export class ApiClient { return buffer.toString(responseEncoding); } - private toHeaderDict(headers: FetchHeaders): NodeJS.Dict { + private toHeaderDict(headers: Headers): NodeJS.Dict { const normalizedHeaders: NodeJS.Dict = {}; headers.forEach((value, key) => { @@ -223,15 +213,6 @@ export class ApiClient { return normalizedHeaders; } - private getFetch(): Fetcher { - const fetcher = (globalThis as { fetch?: Fetcher }).fetch; - if (!fetcher) { - throw new Error('Global fetch API is not available. Please use Node.js 18+.'); - } - - return fetcher; - } - private normalizeFetchError(error: unknown): Error { if (error instanceof Error) { const mutableError = error as Error & { code?: string; cause?: unknown; name: string }; From 37684682b38610a4f19767bf61d89b219bf544a4 Mon Sep 17 00:00:00 2001 From: Ivan Kamkin <234-Ivan.Kamkin@users.noreply.git.saltov.dynabic.com> Date: Thu, 5 Feb 2026 12:29:36 +0500 Subject: [PATCH 8/8] Format example.js --- example.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.js b/example.js index 7f60991..8983627 100644 --- a/example.js +++ b/example.js @@ -10,7 +10,7 @@ const config = new Barcode.Configuration( async function generateBarcode(api) { const request = new Barcode.GenerateRequestWrapper( - Barcode.EncodeBarcodeType.Qr, + Barcode.EncodeBarcodeType.Qr, 'Aspose.BarCode for Cloud Sample'); const oneBarcode = await api.generate(request);