Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: 52799bf1f9a1c387a74baeecac6c1c08f22bb8abdd2a1f0e689d8ed374b47635
checksum: 61066aedc29ef5bd8904d1ee2384dad828e8f9aab1a6b0360797ec7926e7f8dd
- filename: .husky/pre-commit
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
- filename: test/request.spec.ts
checksum: 87afd3bb570fd52437404cbe69a39311ad8a8c73bca9d075ecf88652fd3e13f6
- filename: src/lib/request.ts
checksum: 86d761c4f50fcf377e52c98e0c4db6f06be955790fc5a0f2ba8fe32a88c60825
- filename: src/lib/retryPolicy/delivery-sdk-handlers.ts
checksum: 08ccd6342b3adbeb7b85309a034b4df4b2ad905a0cc2a3778ab483b61ba41b9e
- filename: test/retryPolicy/delivery-sdk-handlers.spec.ts
checksum: 6d22d7482aa6dccba5554ae497e5b0c3572357a5cead6f4822ee4428edc12207
version: ""
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## Change log

### Version: 1.3.3
#### Date: Oct-27-2025
- Fix: Used common serialize method for query params

### Version: 1.3.1
#### Date: Sept-01-2025
- Fix: Replace URLSearchParams.set() with React Native compatible implementation
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/core",
"version": "1.3.1",
"version": "1.3.3",
"type": "commonjs",
"main": "./dist/cjs/src/index.js",
"types": "./dist/cjs/src/index.d.ts",
Expand All @@ -20,12 +20,12 @@
"husky-check": "npx husky install && chmod +x .husky/pre-commit"
},
"dependencies": {
"axios": "^1.11.0",
"axios": "^1.12.2",
"axios-mock-adapter": "^2.1.0",
"husky": "^9.1.7",
"lodash": "^4.17.21",
"qs": "^6.14.0",
"tslib": "^2.8.1",
"husky": "^9.1.7"
"tslib": "^2.8.1"
},
"files": [
"dist/*",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './lib/contentstack-core';
export * from './lib/types';
export * from './lib/contentstack-error';
export * from './lib/api-error';
export * from './lib/request';
export * from './lib/retryPolicy/delivery-sdk-handlers';
72 changes: 72 additions & 0 deletions src/lib/api-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Custom error class for API errors with optimized error handling
*/
export class APIError extends Error {
public error_code: number | string;
public status: number;
public error_message: string;

constructor(message: string, error_code: number | string, status: number) {
super(message);
this.name = 'APIError';
this.error_code = error_code;
this.status = status;
this.error_message = message;

// Remove the stack trace completely to avoid showing internal error handling
this.stack = undefined;
}

/**
* Creates an APIError from an Axios error response
* @param err - The Axios error object
* @returns Formatted APIError with meaningful information
*/
static fromAxiosError(err: any): APIError {
if (err.response?.data) {
return APIError.fromResponseData(err.response.data, err.response.status);
} else if (err.message) {
// For network errors or other non-HTTP errors
return new APIError(err.message, err.code || 'NETWORK_ERROR', 0);
} else {
// Fallback for unknown errors
return new APIError('Unknown error occurred', 'UNKNOWN_ERROR', 0);
}
}

/**
* Creates an APIError from response data
* @param responseData - The response data from the API
* @param status - The HTTP status code
* @returns Formatted APIError
*/
static fromResponseData(responseData: any, status: number): APIError {
// Extract error message with fallback chain
const errorMessage = APIError.extractErrorMessage(responseData);

// Extract error code with fallback chain
const errorCode = APIError.extractErrorCode(responseData, status);

return new APIError(errorMessage, errorCode, status);
}

/**
* Extracts error message from response data with multiple fallback options
*/
private static extractErrorMessage(responseData: any): string {
if (responseData.error_message) return responseData.error_message;
if (responseData.message) return responseData.message;
if (responseData.error) return responseData.error;
if (typeof responseData === 'string') return responseData;
return 'Request failed';
}

/**
* Extracts error code from response data with fallback to status
*/
private static extractErrorCode(responseData: any, status: number): number | string {
if (responseData.error_code) return responseData.error_code;
if (responseData.code) return responseData.code;
return status;
}
}
28 changes: 13 additions & 15 deletions src/lib/request.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,14 @@
import { AxiosInstance } from './types';
import { serialize } from './param-serializer';
import { APIError } from './api-error';

/**
* Handles array parameters properly with & separators
* React Native compatible implementation without URLSearchParams.set()
*/
function serializeParams(params: any): string {
if (!params) return '';

const parts: string[] = [];
Object.keys(params).forEach(key => {
const value = params[key];
if (Array.isArray(value)) {
value.forEach(item => {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(item)}`);
});
} else if (value !== null && value !== undefined) {
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
}
});

return parts.join('&');
return serialize(params);
}

/**
Expand Down Expand Up @@ -48,6 +37,15 @@ async function makeRequest(instance: AxiosInstance, url: string, requestConfig:
}
}

/**
* Handles and formats errors from Axios requests
* @param err - The error object from Axios
* @returns Formatted error object with meaningful information
*/
function handleRequestError(err: any): Error {
return APIError.fromAxiosError(err);
}

export async function getData(instance: AxiosInstance, url: string, data?: any) {
try {
if (instance.stackConfig && instance.stackConfig.live_preview) {
Expand Down Expand Up @@ -87,6 +85,6 @@ export async function getData(instance: AxiosInstance, url: string, data?: any)
throw response;
}
} catch (err: any) {
throw err;
throw handleRequestError(err);
}
}
27 changes: 21 additions & 6 deletions src/lib/retryPolicy/delivery-sdk-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,17 @@ export const retryResponseErrorHandler = (error: any, config: any, axiosInstance
}
error.config.retryCount = retryCount;

return axiosInstance(error.config);
// Apply configured delay for retries
return new Promise((resolve, reject) => {
setTimeout(async () => {
try {
const retryResponse = await axiosInstance(error.config);
resolve(retryResponse);
} catch (retryError) {
reject(retryError);
}
}, config.retryDelay || 300); // Use configured delay with fallback
});
}
}

Expand All @@ -99,17 +109,22 @@ export const retryResponseErrorHandler = (error: any, config: any, axiosInstance
}
};
const retry = (error: any, config: any, retryCount: number, retryDelay: number, axiosInstance: AxiosInstance) => {
let delayTime: number = retryDelay;
if (retryCount > config.retryLimit) {
return Promise.reject(error);
}

delayTime = config.retryDelay;
// Use the passed retryDelay parameter first, then config.retryDelay, then default
const delayTime = retryDelay || config.retryDelay || 300;
error.config.retryCount = retryCount;

return new Promise(function (resolve) {
return setTimeout(function () {
return resolve(axiosInstance(error.request));
return new Promise(function (resolve, reject) {
return setTimeout(async function () {
try {
const retryResponse = await axiosInstance(error.config);
resolve(retryResponse);
} catch (retryError) {
reject(retryError);
}
}, delayTime);
});
};
Expand Down
1 change: 1 addition & 0 deletions test/contentstack-core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ describe('contentstackCore', () => {
it('should call the onError function when an error occurs', async () => {
const onError = jest.fn();
const options = {
defaultHostname: 'cdn.contentstack.io',
onError,
};

Expand Down
Loading
Loading