Skip to content
Merged
4 changes: 3 additions & 1 deletion .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ fileignoreconfig:
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: af3bffd9f46b71daf5b8dd0b3eace75dfd367e359b11503259dc00a8c7105bf0
checksum: 59653dc17458f6bddffb6178bfb8f8191ad4715f41a788082a11d3b08e966b4c
- filename: .husky/pre-commit
checksum: 5baabd7d2c391648163f9371f0e5e9484f8fb90fa2284cfc378732ec3192c193
- filename: test/request.spec.ts
Expand All @@ -14,4 +14,6 @@ fileignoreconfig:
checksum: 08ccd6342b3adbeb7b85309a034b4df4b2ad905a0cc2a3778ab483b61ba41b9e
- filename: test/retryPolicy/delivery-sdk-handlers.spec.ts
checksum: 6d22d7482aa6dccba5554ae497e5b0c3572357a5cead6f4822ee4428edc12207
- filename: test/contentstack-core.spec.ts
checksum: 2d1e0f63ad8ea37890de2aa6c7e394c83488888f4a40ad7a71eeba2290b95924
version: ""
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
## Change log

### Version: 1.3.8
#### Date: Jan-12-2026
- Fix: Add .js extensions to relative imports in ESM build for proper module resolution
- Fix: Change lodash import from named import to default import for ESM compatibility with CommonJS modules

### Version: 1.3.7
#### Date: Jan-12-2026
- Fix: Improve error messages


### Version: 1.3.8
#### Date: Jan-15-2026
- Fix: Add .js extensions to relative imports in ESM build for proper module resolution
- Fix: Change lodash import from named import to default import for ESM compatibility with CommonJS modules

### Version: 1.3.7
#### Date: Jan-12-2026
- Fix: Improve error messages
Expand Down
114 changes: 98 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,128 @@
# @contentstack/core

[![Contentstack](https://www.contentstack.com/docs/static/images/contentstack.png)](https://www.contentstack.com/)
## TypeScript Core SDK for Contentstack

TypeScript Core SDK for Contentstack - A foundational library providing core modules and utilities for Contentstack TypeScript SDKs.

## About Contentstack

Contentstack is a headless CMS with an API-first approach. It is a CMS that developers can use to build powerful cross-platform applications in their favorite languages. Build your application frontend, and Contentstack will take care of the rest. [Read More](https://www.contentstack.com/).

### Prerequisite
## Description

You need Node.js version 4.4.7 or later installed to use the Contentstack TS Core SDK.
This package contains core modules and utilities used by the [Contentstack TypeScript Delivery SDK](https://github.com/contentstack/contentstack-typescript/). It provides essential functionality including HTTP client configuration, error handling, request management, parameter serialization, and retry policies.

## Installation
## Features

```
- **HTTP Client**: Configurable Axios-based HTTP client with support for custom adapters
- **Error Handling**: Comprehensive error classes for API and Contentstack-specific errors
- **Request Management**: Request handling with interceptors and custom error callbacks
- **Parameter Serialization**: Custom parameter serialization for API requests
- **Retry Policies**: Built-in retry logic for handling rate limits and transient errors
- **TypeScript Support**: Full TypeScript definitions included
- **Multiple Build Formats**: Supports CommonJS, ESM, UMD, and TypeScript declarations

## Important Note

**This package is an internal dependency** used by Contentstack TypeScript SDKs. End users should **not** install this package directly. Instead, install the appropriate Contentstack SDK (e.g., [Contentstack TypeScript Delivery SDK](https://github.com/contentstack/contentstack-typescript/)), which will automatically include this package as a dependency.

## For SDK Developers

If you are developing or maintaining a Contentstack SDK and need to use this core package directly, you can install it as a dependency:

```bash
npm install @contentstack/core
```

## Use case
Then import the modules:

This package contains some core modules and utilities used by the [Contentstack Typescript Delivery SDK](https://github.com/contentstack/contentstack-javascript/) SDK.
```typescript
import {
httpClient,
// ... other exports
} from '@contentstack/core';
```

## Development

### Create the build:
### Prerequisites

- Node.js version 4.4.7 or later

### Setup

Clone the repository and install dependencies:

```bash
git clone https://github.com/contentstack/contentstack-js-core.git
cd contentstack-js-core
npm install
```

### Build

Build all output formats (CommonJS, ESM, UMD, and TypeScript declarations):

```bash
npm run build
```

### Run Scripts:

Run the unit tests:
Build specific formats:

```bash
npm run build # Build all
npm run build:cjs # CommonJS
npm run build:esm # ES Modules
npm run build:umd # UMD
npm run build:types # TypeScript declarations
```

### Testing

Run unit tests:

```bash
npm run test
```

Run the lint tests:
Run linting:

```
```bash
npm run lint
```

Pack the SDK:
### Packaging

```
Create a package tarball:

```bash
npm run package
```
```

### Clean

Clean build artifacts:

```bash
npm run clean
```

## License

This project is licensed under the MIT License. See the [LICENSE.txt](LICENSE.txt) file for details.

## Repository

- **GitHub**: [contentstack/contentstack-js-core](https://github.com/contentstack/contentstack-js-core)

## Related Projects

- [Contentstack TypeScript Delivery SDK](https://github.com/contentstack/contentstack-javascript/)

## Support

For issues and feature requests, please visit the [GitHub Issues](https://github.com/contentstack/contentstack-js-core/issues) page.

---

Copyright (c) 2016-2025 Contentstack. All rights reserved.
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/core",
"version": "1.3.7",
"version": "1.3.8",
"type": "commonjs",
"main": "./dist/cjs/src/index.js",
"types": "./dist/cjs/src/index.d.ts",
Expand Down
5 changes: 3 additions & 2 deletions src/lib/contentstack-core.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cloneDeep } from 'lodash';
import _ from 'lodash';
import { serialize } from './param-serializer';
import axios, { AxiosRequestHeaders, getAdapter } from 'axios';
import { AxiosInstance, HttpClientParams } from './types';
Expand All @@ -20,6 +20,7 @@ export function httpClient(options: HttpClientParams): AxiosInstance {
const title = [data.name, data.message].filter((a) => a).join(' - ');
console.error(ERROR_MESSAGES.CONSOLE.ERROR_WITH_TITLE(title));
}

return;
}
if (data !== undefined) {
Expand All @@ -38,7 +39,7 @@ export function httpClient(options: HttpClientParams): AxiosInstance {

const config: HttpClientParams = {
...defaultConfig,
...cloneDeep(options),
..._.cloneDeep(options),
};

if (config.apiKey && config.headers) {
Expand Down
83 changes: 77 additions & 6 deletions test/contentstack-core.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { AxiosInstance } from '../src';
import { httpClient } from '../src/lib/contentstack-core';
import MockAdapter from 'axios-mock-adapter';
describe('contentstackCore', () => {
it('should return default config when no config is passed', (done) => {
const client = httpClient({});
done();
it('should return default config when no config is passed', () => {
httpClient({});
});

describe('logHandler', () => {
Expand All @@ -17,7 +16,9 @@ describe('contentstackCore', () => {
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
httpClient({}).defaults.logHandler('error', error);

expect(consoleErrorSpy).toHaveBeenCalledWith('Error: Error - Something went wrong. Review the error details and try again.');
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error: Error - Something went wrong. Review the error details and try again.'
);

consoleErrorSpy.mockRestore();
});
Expand Down Expand Up @@ -152,7 +153,7 @@ describe('contentstackCore', () => {
it('should call the onError function when an error occurs', async () => {
// Suppress expected console.error from network error
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();

const onError = jest.fn();
const options = {
defaultHostname: 'cdn.contentstack.io',
Expand All @@ -166,7 +167,7 @@ describe('contentstackCore', () => {
} catch (error: unknown) {
expect(onError).toBeCalledWith(error);
}

consoleErrorSpy.mockRestore();
});

Expand All @@ -188,4 +189,74 @@ describe('contentstackCore', () => {
expect(client.httpClientParams.onError).not.toBeCalled();
});
});

describe('config deep cloning', () => {
it('should properly handle nested objects in params using cloneDeep', () => {
const options = {
defaultHostname: 'example.com',
params: {
environment: 'test',
nested: {
level1: {
level2: {
value: 'deep-nested',
},
},
},
},
};

const instance = httpClient(options);

// Verify nested structure is properly accessible
// This test ensures cloneDeep is working correctly (ESM import fix)
expect(instance.httpClientParams.params?.nested?.level1?.level2?.value).toBe('deep-nested');
expect(instance.httpClientParams.params?.environment).toBe('test');
});

it('should handle complex nested structures in params', () => {
const complexOptions = {
defaultHostname: 'example.com',
params: {
environment: 'production',
filters: {
category: {
name: 'tech',
tags: ['javascript', 'typescript'],
},
},
},
};

const instance = httpClient(complexOptions);

// Verify complex nested structure is properly handled
expect(instance.httpClientParams.params?.filters?.category?.name).toBe('tech');
expect(instance.httpClientParams.params?.filters?.category?.tags).toEqual(['javascript', 'typescript']);
});

it('should work correctly with lodash cloneDeep import (ESM compatibility)', () => {
// This test verifies that the lodash import works correctly in ESM
// by ensuring nested object cloning works as expected
const options = {
defaultHostname: 'example.com',
params: {
query: {
type: 'entry',
include: {
count: true,
schema: true,
},
},
},
};

const instance = httpClient(options);

// If cloneDeep wasn't working (due to import issues), this would fail
expect(instance.httpClientParams.params?.query?.type).toBe('entry');
expect(instance.httpClientParams.params?.query?.include?.count).toBe(true);
expect(instance.httpClientParams.params?.query?.include?.schema).toBe(true);
});
});
});
2 changes: 1 addition & 1 deletion test/esm-exports.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('ESM Exports Tests', () => {

it('should re-export getData from ESM index.js', () => {
const indexContent = fs.readFileSync(esmIndexPath, 'utf-8');
expect(indexContent).toContain("export * from './lib/request'");
expect(indexContent).toContain("export * from './lib/request.js'");
});

it('should verify getData is a named export in ESM build', () => {
Expand Down
Loading
Loading