Skip to content

Commit c96a5f6

Browse files
Merge branch 'development' into feat/taxonomy-publishing
2 parents 97d0aa2 + 90748d1 commit c96a5f6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+6202
-1261
lines changed

.talismanrc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fileignoreconfig:
99
ignore_detectors:
1010
- filecontent
1111
- filename: package-lock.json
12-
checksum: bab53d56ce2609e960fdbbd1e87cc89915820e6761016ddd74ee57f931f4223d
12+
checksum: 17b5bbabcc58beaa180a7fa931fc3fb407ee0e3447d47da224f60118c0a4c294
1313
- filename: .husky/pre-commit
1414
checksum: 52a664f536cf5d1be0bea19cb6031ca6e8107b45b6314fe7d47b7fad7d800632
1515
- filename: test/sanity-check/api/user-test.js
@@ -30,4 +30,11 @@ fileignoreconfig:
3030
checksum: b76ca091caa3a1b2658cd422a2d8ef3ac9996aea0aff3f982d56bb309a3d9fde
3131
- filename: test/unit/ContentstackClient-test.js
3232
checksum: 974a4f335aef025b657d139bb290233a69bed1976b947c3c674e97baffe4ce2f
33+
- filename: test/unit/ContentstackHTTPClient-test.js
34+
checksum: 4043efd843e24da9afd0272c55ef4b0432e3374b2ca12b913f1a6654df3f62be
35+
- filename: test/unit/contentstack-test.js
36+
checksum: 2597efae3c1ab8cc173d5bf205f1c76932211f8e0eb2a16444e055d83481976c
3337
version: "1.0"
38+
39+
40+

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## [v1.27.2](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.2) (2026-01-12)
4+
- Enhancement
5+
- Improved error messages
6+
- params support in entry variants update
7+
8+
## [v1.27.1](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.1) (2026-01-5)
9+
- Fix
10+
- Resolve qs dependency version
11+
12+
## [v1.27.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.0) (2025-12-15)
13+
- Enhancement
14+
- Refactored region endpoint resolution to use centralized `@contentstack/utils` package
15+
- Improved region handling flexibility by leveraging shared utility functions
16+
17+
## [v1.26.0](https://github.com/contentstack/contentstack-management-javascript/tree/v1.26.0) (2025-10-20)
18+
- Enhancement
19+
- Added taxonomy localization support
20+
321
## [v1.25.1](https://github.com/contentstack/contentstack-management-javascript/tree/v1.25.1) (2025-10-06)
422
- Fix
523
- Updated delay handling to use centralized external configuration in SDK interceptor

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2012-2025 Contentstack
3+
Copyright (c) 2012-2026 Contentstack
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

lib/contentstack.js

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,7 @@ import clonedeep from 'lodash/cloneDeep'
77
import getUserAgent from './core/Util.js'
88
import contentstackClient from './contentstackClient.js'
99
import httpClient from './core/contentstackHTTPClient.js'
10-
const regionHostMap = {
11-
NA: 'api.contentstack.io',
12-
EU: 'eu-api.contentstack.com',
13-
AU: 'au-api.contentstack.com',
14-
AZURE_NA: 'azure-na-api.contentstack.com',
15-
AZURE_EU: 'azure-eu-api.contentstack.com',
16-
GCP_NA: 'gcp-na-api.contentstack.com',
17-
GCP_EU: 'gcp-eu-api.contentstack.com'
18-
}
10+
import { getContentstackEndpoint } from '@contentstack/utils'
1911

2012
/**
2113
* Create client instance
@@ -27,7 +19,10 @@ const regionHostMap = {
2719
* const client = contentstack.client()
2820
*
2921
* @param {object} params - Client initialization parameters
30-
* @param {Object=} param.proxy -
22+
* @param {Object=} params.proxy - Proxy configuration object for HTTP requests
23+
* @param {string=} params.region - Region for API endpoint (NA, EU, AU, AZURE_NA, AZURE_EU, GCP_NA, GCP_EU)
24+
* @param {string=} params.feature - Feature identifier for user agent
25+
* @param {string=} params.refreshToken - Refresh token for OAuth authentication
3126
* @prop {string=} params.endpoint - API endpoint that a service will talk to
3227
* @example //Set the `endpoint` to 'https://api.contentstack.io:{port}/{version}'
3328
* import * as contentstack from '@contentstack/management'
@@ -48,7 +43,7 @@ const regionHostMap = {
4843
* import * as contentstack from '@contentstack/management'
4944
* const client = contentstack.client({ authtoken: 'value' })
5045
*
51-
* @prop {string=} params.early_access - Optional early_access is a token used for early access of new features in CMA requests.
46+
* @prop {Array<string>=} params.early_access - Optional array of early access tokens used for early access of new features in CMA requests.
5247
* @example //Set the `early_access`
5348
* import * as contentstack from '@contentstack/management'
5449
* const client = contentstack.client({ early_access: ['ea1', 'ea2'] })
@@ -127,7 +122,7 @@ const regionHostMap = {
127122
* const client = contentstack.client({ logHandler: (level, data) => {
128123
if (level === 'error' && data) {
129124
const title = [data.name, data.message].filter((a) => a).join(' - ')
130-
console.error(`[error] ${title}`)
125+
console.error(`An error occurred due to ${title}. Review the details and try again.`)
131126
return
132127
}
133128
console.log(`[${level}] ${data}`)
@@ -167,21 +162,44 @@ const regionHostMap = {
167162
*
168163
* @prop {string=} params.application - Application name and version e.g myApp/version
169164
* @prop {string=} params.integration - Integration name and version e.g react/version
170-
* @returns Contentstack.Client
165+
* @prop {string=} params.region - API region. Valid values: 'na', 'eu', 'au', 'azure_na', 'azure_eu', 'gcp_na', 'gcp_eu' (default: 'na')
166+
* @example //Set the `region` to 'eu'
167+
* import * as contentstack from '@contentstack/management'
168+
* const client = contentstack.client({ region: 'eu' })
169+
*
170+
* @prop {string=} params.feature - Feature identifier for user agent header
171+
* @prop {Array<Object>=} params.plugins - Optional array of plugin objects. Each plugin must have `onRequest` and `onResponse` methods.
172+
* @example //Set plugins to intercept and modify requests/responses
173+
* import * as contentstack from '@contentstack/management'
174+
* const client = contentstack.client({
175+
* plugins: [
176+
* {
177+
* onRequest: (request) => {
178+
* // Return modified request
179+
* return {
180+
* ...request,
181+
* headers: {
182+
* ...request.headers,
183+
* 'X-Custom-Header': 'value'
184+
* }
185+
* }
186+
* },
187+
* onResponse: (response) => {
188+
* // Return modified response
189+
* return response
190+
* }
191+
* }
192+
* ]
193+
* })
194+
* @returns {ContentstackClient} Instance of ContentstackClient
171195
*/
172196
export function client (params = {}) {
173-
let defaultHostName
197+
let defaultHostName = params.region
198+
? getContentstackEndpoint(params.region.toUpperCase(), 'contentManagement', true)
199+
: getContentstackEndpoint('NA', 'contentManagement', true)
174200

175-
if (params.region) {
176-
const region = params.region.toUpperCase()
177-
if (!regionHostMap[region]) {
178-
throw new Error(`Invalid region '${params.region}' provided. Allowed regions are: ${Object.keys(regionHostMap).join(', ')}`)
179-
}
180-
defaultHostName = regionHostMap[region]
181-
} else if (params.host) {
201+
if (params.host) {
182202
defaultHostName = params.host
183-
} else {
184-
defaultHostName = regionHostMap['NA']
185203
}
186204

187205
const defaultParameter = {

lib/contentstackClient.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@ export default function contentstackClient ({ http }) {
1919
* @prop {string} parameters.password - password for user to login
2020
* @prop {string} parameters.tfa_token - tfa token for user to login (2FA token)
2121
* @prop {string} parameters.mfaSecret - TOTP secret key for generating 2FA token
22-
* @returns {Promise}
22+
* @param {Object=} params - Optional request parameters
23+
* @returns {Promise<Object>} Promise for login response
2324
* @example
2425
* import * as contentstack from '@contentstack/management'
2526
* const client = contentstack.client()
2627
*
2728
* client.login({ email: <emailid>, password: <password> })
28-
* .then(() => console.log('Logged in successfully'))
29+
* .then(() => console.log('Login successful.'))
2930
*
3031
* @example
3132
* client.login({ email: <emailid>, password: <password>, tfa_token: <tfa_token> })
32-
* .then(() => console.log('Logged in successfully'))
33+
* .then(() => console.log('Login successful.'))
3334
*
3435
* @example
3536
* client.login({ email: <emailid>, password: <password>, mfaSecret: <mfa_secret> })
36-
* .then(() => console.log('Logged in successfully'))
37+
* .then(() => console.log('Login successful.'))
3738
*/
3839
function login (requestBody = {}, params = {}) {
3940
http.defaults.versioningStrategy = 'path'
@@ -59,7 +60,8 @@ export default function contentstackClient ({ http }) {
5960
* The information returned includes details of the stacks owned by and shared with the specified user account.
6061
* @memberof ContentstackClient
6162
* @func getUser
62-
* @returns {Promise}
63+
* @param {Object=} params - Optional request parameters
64+
* @returns {Promise<Object>} Promise for User instance
6365
* @example
6466
* import * as contentstack from '@contentstack/management'
6567
* const client = contentstack.client()
@@ -78,10 +80,11 @@ export default function contentstackClient ({ http }) {
7880
* @description Get Stack instance. A stack is a space that stores the content of a project.
7981
* @memberof ContentstackClient
8082
* @func stack
81-
* @param {String} api_key - Stack API Key
82-
* @param {String} management_token - Management token for Stack.
83-
* @param {String} branch_name - Branch name or alias to access specific branch. Default is master.
84-
* @returns {Stack} Instance of Stack
83+
* @param {Object=} params - Stack initialization parameters
84+
* @prop {String} params.api_key - Stack API Key
85+
* @prop {String} params.management_token - Management token for Stack.
86+
* @prop {String} params.branch_uid - Branch UID or alias to access specific branch. Default is master.
87+
* @returns {Stack} Instance of Stack.
8588
*
8689
* @example
8790
* import * as contentstack from '@contentstack/management'
@@ -121,7 +124,7 @@ export default function contentstackClient ({ http }) {
121124
* @description Organization is the top-level entity in the hierarchy of Contentstack, consisting of stacks and stack resources, and users.
122125
* @memberof ContentstackClient
123126
* @func organization
124-
* @param {String} uid - Organization UID.
127+
* @param {String=} uid - Organization UID. If not provided, returns organization instance for querying all organizations.
125128
* @returns {Organization} Instance of Organization.
126129
*
127130
* @example
@@ -152,7 +155,7 @@ export default function contentstackClient ({ http }) {
152155
* @memberof ContentstackClient
153156
* @param {String} authtoken - Authtoken to logout from.
154157
* @func logout
155-
* @returns {Object} Response object.
158+
* @returns {Promise<Object>} Promise for response object.
156159
*
157160
* @example
158161
* import * as contentstack from '@contentstack/management'
@@ -195,19 +198,19 @@ export default function contentstackClient ({ http }) {
195198
* @memberof ContentstackClient
196199
* @func oauth
197200
* @param {Object} parameters - oauth parameters
198-
* @prop {string} parameters.appId - appId of the application
199-
* @prop {string} parameters.clientId - clientId of the application
200-
* @prop {string} parameters.clientId - clientId of the application
201-
* @prop {string} parameters.responseType - responseType
202-
* @prop {string} parameters.scope - scope
203-
* @prop {string} parameters.clientSecret - clientSecret of the application
204-
* @returns {OAuthHandler} Instance of OAuthHandler
201+
* @prop {string=} parameters.appId - appId of the application (default: '6400aa06db64de001a31c8a9')
202+
* @prop {string=} parameters.clientId - clientId of the application (default: 'Ie0FEfTzlfAHL4xM')
203+
* @prop {string=} parameters.redirectUri - redirect URI for OAuth callback (default: 'http://localhost:8184')
204+
* @prop {string=} parameters.responseType - responseType (default: 'code')
205+
* @prop {string=} parameters.scope - scope
206+
* @prop {string=} parameters.clientSecret - clientSecret of the application
207+
* @returns {OAuthHandler} Instance of OAuthHandler.
205208
* @example
206209
* import * as contentstack from '@contentstack/management'
207210
* const client = contentstack.client()
208211
*
209212
* client.oauth({ appId: <appId>, clientId: <clientId>, redirectUri: <redirectUri>, clientSecret: <clientSecret>, responseType: <responseType>, scope: <scope> })
210-
* .then(() => console.log('Logged in successfully'))
213+
* .then(() => console.log('Login successful.'))
211214
*
212215
*/
213216
function oauth (params = {}) {

lib/contentstackCollection.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
* @namespace ContentstackCollection
33
*/
44
export default class ContentstackCollection {
5+
/**
6+
* Creates a ContentstackCollection instance.
7+
* @param {Object} response - HTTP response object.
8+
* @param {Object} http - HTTP client instance.
9+
* @param {Object=} stackHeaders - Stack headers to include in data.
10+
* @param {Function=} wrapperCollection - Collection wrapper function to transform items.
11+
*/
512
constructor (response, http, stackHeaders = null, wrapperCollection) {
613
const data = response.data || {}
714
if (stackHeaders) {

lib/core/Util.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { platform, release } from 'os'
2+
import { ERROR_MESSAGES } from './errorMessages'
3+
24
const HOST_REGEX = /^(?!(?:(?:https?|ftp):\/\/|internal|localhost|(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))(?:[\w-]+\.contentstack\.(?:io|com)(?::[^\/\s:]+)?|[\w-]+(?:\.[\w-]+)*(?::[^\/\s:]+)?)(?![\/?#])$/ // eslint-disable-line
35

46
export function isHost (host) {
@@ -217,7 +219,7 @@ const isAllowedHost = (hostname) => {
217219

218220
export const validateAndSanitizeConfig = (config) => {
219221
if (!config?.url || typeof config?.url !== 'string') {
220-
throw new Error('Invalid request configuration: missing or invalid URL')
222+
throw new Error(ERROR_MESSAGES.INVALID_URL_CONFIG)
221223
}
222224

223225
// Validate the URL to prevent SSRF attacks
@@ -235,3 +237,26 @@ export const validateAndSanitizeConfig = (config) => {
235237
url: config.url.trim() // Sanitize URL by removing whitespace
236238
}
237239
}
240+
241+
/**
242+
* Normalizes and validates plugin array
243+
* @param {Array|undefined} plugins - Array of plugin objects
244+
* @returns {Array} Normalized array of plugins
245+
*/
246+
export function normalizePlugins (plugins) {
247+
if (!plugins) {
248+
return []
249+
}
250+
251+
if (!Array.isArray(plugins)) {
252+
return []
253+
}
254+
255+
return plugins.filter(plugin => {
256+
if (!plugin || typeof plugin !== 'object') {
257+
return false
258+
}
259+
// Plugin must have both onRequest and onResponse methods
260+
return typeof plugin.onRequest === 'function' && typeof plugin.onResponse === 'function'
261+
})
262+
}

0 commit comments

Comments
 (0)