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
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
115 changes: 99 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,129 @@
# @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-javascript/). 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,
ContentstackError,
ApiError,
// ... 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: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);
});
});
});
Loading
Loading