diff --git a/Agents.md b/Agents.md
new file mode 100644
index 0000000..5b3175f
--- /dev/null
+++ b/Agents.md
@@ -0,0 +1,16 @@
+# Aspose.BarCode Cloud SDK and Codegen repository
+
+This is repository with swagger generated SDK for Aspose.Barcode.Cloud service and code generating scripts. All sdk are submodules of this repo and located in `submodules` directory. Swagger specification of service API is located in `spec/aspose-barcode-cloud.json` file. Custom mustashe templates are located in `codegen/Templates` dir. Original templates for all languages are in github. Sripts for generating SDK `codegen` directory. Some post processing are in Makefiles in all submodules repo.
+
+## Common requirements for making changes in SDK code
+
+0. Don't commit or push in repo by youreself. But you can pull and stage in git.
+1. To run any scripts, use WSL if you're on Windows.
+2. After making changes run tests with `make test` or similar comand in Makefile in SDK submodule repo.
+3. Add changes to mustache templates after changes in SDK code are made. If you changed some code and template for it not in codegen/Templates dir. Download this template and made changes in local copy in codegen/Templates dir.
+4. Enshure that generated code is the same as you new code. For it:
+ 4.1 Stage your changes in sdk submodule repo.
+ 4.2 Run `make ` command in main repo. See Makefile for it.
+ 4.3 After generating script are end it work. Enshure there is no unstaged changes in sdk submodule.
+ 4.4 Fix templates if generated code are not the same as you new code.
+5. After templates fixed, you can end your task.
diff --git a/codegen/Templates/nodejs/api.mustache b/codegen/Templates/nodejs/api.mustache
index 3561e1e..8be1046 100644
--- a/codegen/Templates/nodejs/api.mustache
+++ b/codegen/Templates/nodejs/api.mustache
@@ -1,6 +1,5 @@
import { Configuration } from './Configuration';
-import { HttpClient, HttpOptions, HttpResponse, HttpResult } from './httpClient';
-import { Multipart, RequestFile, FormParamsType } from './multipart';
+import { Multipart, RequestFile, FormParamPairs } from './multipart';
export * from './models';
@@ -9,6 +8,224 @@ import { {{#models}} {{#model}}{{classname}},{{/model}} {{/models}} } from './mo
import { {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} {{operationIdCamelCase}}RequestWrapper, {{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} } from './models';
+type StringMap = Record;
+
+type ApiRequestOptions = {
+ uri: string;
+ body?: any;
+ encoding?: BufferEncoding | null;
+ form?: StringMap;
+ headers?: StringMap;
+ json?: boolean;
+ method?: string;
+ qs?: StringMap;
+};
+
+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;
+};
+
+export class ApiClient {
+ private readonly _fetcher: typeof fetch;
+
+ 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+.');
+ }
+
+ this._fetcher = resolvedFetch;
+ }
+
+ 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: RequestInit = {
+ 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: RequestInit,
+ responseEncoding: BufferEncoding | null
+ ): Promise {
+ let response: Response;
+ try {
+ response = await this._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: Response,
+ 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: Headers): 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 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 {
@@ -184,11 +401,11 @@ export class {{classname}} {
'x-aspose-client-version': '{{npmVersion}}'
};
protected _configuration: Configuration;
- private _client: HttpClient;
+ private _client: ApiClient;
constructor(configuration: Configuration) {
this._configuration = configuration;
- this._client = new HttpClient();
+ this._client = new ApiClient();
}
{{#operation}}
@@ -200,7 +417,7 @@ export class {{classname}} {
{{/summary}}
* @param request {{operationIdCamelCase}}RequestWrapper
*/
- public async {{nickname}}(request: {{operationIdCamelCase}}RequestWrapper): Promise<{ response: HttpResponse; {{#returnType}}body: {{{returnType}}}; {{/returnType}}{{^returnType}}body?: any; {{/returnType}} }> {
+ public async {{nickname}}(request: {{operationIdCamelCase}}RequestWrapper): Promise<{ response: ApiResponse; {{#returnType}}body: {{{returnType}}}; {{/returnType}}{{^returnType}}body?: any; {{/returnType}} }> {
const requestPath =
this._configuration.getApiBaseUrl() +
'{{{path}}}'{{#pathParams}}.replace(
@@ -211,7 +428,7 @@ export class {{classname}} {
let queryParameters: any = {};
let headerParams: any = (Object as any).assign({}, this.defaultHeaders);
{{#hasFormParams}}
- const formParams: FormParamsType = [];
+ const formParams: FormParamPairs = [];
{{/hasFormParams}}
{{#allParams}}
@@ -247,7 +464,7 @@ export class {{classname}} {
}
{{/isFile}}
{{/formParams}}
- const requestOptions: HttpOptions = {
+ const requestOptions: ApiRequestOptions = {
method: '{{httpMethod}}',
qs: queryParameters,
headers: headerParams,
@@ -272,7 +489,7 @@ export class {{classname}} {
await this._configuration.authentication.applyToRequestAsync(requestOptions);
- const result: HttpResult = await this._client.requestAsync(requestOptions);
+ const result: ApiResult = await this._client.requestAsync(requestOptions);
{{#returnType}}
return {
diff --git a/codegen/Templates/nodejs/docs/tsconfig.mustache b/codegen/Templates/nodejs/docs/tsconfig.mustache
index 403750e..3963bc2 100644
--- a/codegen/Templates/nodejs/docs/tsconfig.mustache
+++ b/codegen/Templates/nodejs/docs/tsconfig.mustache
@@ -27,6 +27,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).
diff --git a/codegen/Templates/nodejs/multipart.mustache b/codegen/Templates/nodejs/multipart.mustache
new file mode 100644
index 0000000..5a4aaa3
--- /dev/null
+++ b/codegen/Templates/nodejs/multipart.mustache
@@ -0,0 +1,57 @@
+import crypto from 'crypto';
+
+type StringMap = Record;
+
+export interface FormParamPairs extends Array> {}
+
+interface IRequestFile {
+ name: string;
+ filename: string;
+ data: Buffer;
+ contentType?: string;
+}
+
+export class RequestFile implements IRequestFile {
+ constructor(
+ readonly name: string,
+ readonly filename: string,
+ readonly data: Buffer,
+ readonly contentType?: string
+ ) {}
+}
+
+export class Multipart {
+ readonly boundary: string;
+ readonly body: Buffer;
+ readonly headers: StringMap;
+
+ constructor(textFields: FormParamPairs, files?: IRequestFile[]) {
+ const random = crypto.randomUUID();
+ this.boundary = '------------------------' + random.replace(/-/g, '');
+
+ const bodyLines = [];
+ for (const tuple of textFields) {
+ bodyLines.push(`--${this.boundary}`);
+ bodyLines.push(`Content-Disposition: form-data; name="${tuple[0]}"`);
+ bodyLines.push('');
+ bodyLines.push(tuple[1]);
+ }
+ for (const file of files || []) {
+ bodyLines.push(`--${this.boundary}`);
+ bodyLines.push(
+ `Content-Disposition: form-data; name="${file.name}"; filename="${file.filename || 'filename'}"`
+ );
+ bodyLines.push(`Content-Type: ${file.contentType || 'application/octet-stream'}`);
+ bodyLines.push('');
+ bodyLines.push(file.data.toString('binary'));
+ }
+ bodyLines.push(`--${this.boundary}--`);
+
+ this.body = Buffer.from(bodyLines.join('\r\n'), 'binary');
+
+ this.headers = {
+ 'Content-Type': `multipart/form-data; boundary=${this.boundary}`,
+ 'Content-Length': this.body.length.toString(),
+ };
+ }
+}
diff --git a/codegen/Templates/nodejs/package.mustache b/codegen/Templates/nodejs/package.mustache
index c8111a0..2211d12 100644
--- a/codegen/Templates/nodejs/package.mustache
+++ b/codegen/Templates/nodejs/package.mustache
@@ -129,10 +129,11 @@
"dist"
],
"engines": {
- "node": ">=16"
+ "node": ">=18"
},
"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/submodules/node b/submodules/node
index d13cbe9..48afd98 160000
--- a/submodules/node
+++ b/submodules/node
@@ -1 +1 @@
-Subproject commit d13cbe939349f8cf1853dbd8f762423f66168504
+Subproject commit 48afd98ac89bc4100fc468b5c9b38256bfcd2c45