Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5947971
feat: comprehensive API test suite improvements
AniketDev7 Jan 22, 2026
86f3c28
chore: update gitignore and remove env.example.txt
AniketDev7 Jan 23, 2026
bee3a7c
fix: improve bulk operations and branch test reliability
AniketDev7 Jan 23, 2026
b4e9ae2
fix: use dynamic environment names in publish/deploy tests
AniketDev7 Jan 23, 2026
1397d9b
fix: improve test reliability and cleanup logic
AniketDev7 Jan 23, 2026
9f18d9d
feat: add DAM 2.0 asset_fields query parameter test cases
AniketDev7 Feb 10, 2026
42cdbf1
Sanity tests: dynamic setup, report context, fixes, security cleanup
AniketDev7 Feb 10, 2026
6965260
Release v1.27.5: Fix concurrency queue error handling to reject with …
nadeem-cs Feb 11, 2026
7b84e5b
fix: eslint errors
nadeem-cs Feb 11, 2026
d19a4c8
fix: otplib method migration and test cases
nadeem-cs Feb 11, 2026
6e01b08
fix: ensure Expected vs Actual always appears in Mochawesome report
AniketDev7 Feb 11, 2026
c83894c
refactor: revert otplib update; add optional chaining wherever required
nadeem-cs Feb 11, 2026
5828b96
chore(sanity): add trackedExpect across all sanity API test files
AniketDev7 Feb 11, 2026
865d3c7
Merge pull request #504 from contentstack/hotfix/DX-4429
nadeem-cs Feb 11, 2026
cbd293b
Merge origin/master (PR #504 hotfix DX-4429) into feat/dam-2.0-test-c…
AniketDev7 Feb 11, 2026
a297264
fix: add sanity-check mock content-type.js for unit tests
AniketDev7 Feb 11, 2026
1ce0d64
fix: ESLint in asset-test.js and unit test mock paths
AniketDev7 Feb 11, 2026
14c1394
fix: ESLint in branch-test.js and auditlog-test.js
AniketDev7 Feb 11, 2026
ade22b1
fix: ESLint in branchAlias-test.js
AniketDev7 Feb 11, 2026
260e454
chore: disable no-unused-expressions for test files
AniketDev7 Feb 11, 2026
ed7c18e
fix: ESLint in bulkOperation-test.js
AniketDev7 Feb 11, 2026
0a53635
chore: sanity test lint fixes and trackedExpect cleanup
AniketDev7 Feb 11, 2026
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
11 changes: 10 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,14 @@ module.exports = {
'promise'
],
rules: {
}
},
overrides: [
{
files: ['test/**/*.js'],
rules: {
'no-unused-expressions': 'off',
'no-useless-escape': 'off'
}
}
]
}
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ coverage/
test/utility/dataFiles/
test/sanity-check/utility/dataFiles/
report.json
sanity-check-backup/
.vscode/

# TypeScript v1 declaration files
typings/
Expand All @@ -62,10 +64,12 @@ tsconfig.json

# dotenv environment variables file
.env
test-curls.txt

# next.js build output
.next
.dccache
dist
jsdocs
.early.coverage
.early.coverage
docs/
103 changes: 85 additions & 18 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,43 +1,110 @@
fileignoreconfig:
- filename: lib/contentstackClient.js
checksum: f564f6eee5c17dc73abdeab4be226a3b37942893e149d907d2a4ef415c485c5e
- filename: test/unit/globalField-test.js
checksum: 25185e3400a12e10a043dc47502d8f30b7e1c4f2b6b4d3b8b55cdc19850c48bf
- filename: lib/stack/index.js
checksum: 6aab5edf85efb17951418b4dc4402889cd24c8d786c671185074aeb4d50f0242
- filename: test/sanity-check/api/stack-test.js
checksum: 198d5cf7ead33b079249dc3ecdee61a9c57453e93f1073ed0341400983e5aa53
- filename: .github/workflows/secrets-scan.yml
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: 751efa34d2f832c7b99771568b5125d929dab095784b6e4ea659daaa612994c8
checksum: 92b88ce00603ede68344bac6bd6bf76bdb76f1e5f5ba8d1d0c79da2b72c5ecc0
- filename: test/unit/ContentstackClient-test.js
checksum: 5d8519b5b93c715e911a62b4033614cc4fb3596eabf31c7216ecb4cc08604a73
- filename: .husky/pre-commit
checksum: 52a664f536cf5d1be0bea19cb6031ca6e8107b45b6314fe7d47b7fad7d800632
- filename: test/sanity-check/api/user-test.js
checksum: 6bb8251aad584e09f4d963a913bd0007e5f6e089357a44c3fb1529e3fda5509d
- filename: lib/stack/asset/index.js
checksum: b3358310e9cb2fb493d70890b7219db71e2202360be764465d505ef71907eefe
- filename: test/sanity-check/api/previewToken-test.js
checksum: 9a42e079b7c71f76932896a0d2390d86ac626678ab20d36821dcf962820a886c
- filename: lib/stack/deliveryToken/index.js
checksum: 51ae00f07f4cc75c1cd832b311c2e2482f04a8467a0139da6013ceb88fbdda2f
- filename: lib/stack/deliveryToken/previewToken/index.js
checksum: b506f33bffdd20dfc701f964370707f5d7b28a2c05c70665f0edb7b3c53c165b
- filename: examples/robust-error-handling.js
checksum: e8a32ffbbbdba2a15f3d327273f0a5b4eb33cf84cd346562596ab697125bbbc6
- filename: test/sanity-check/api/bulkOperation-test.js
checksum: f40a14c84ab9a194aaf830ca68e14afde2ef83496a07d4a6393d7e0bed15fb0e
- filename: lib/contentstackClient.js
checksum: b76ca091caa3a1b2658cd422a2d8ef3ac9996aea0aff3f982d56bb309a3d9fde
- filename: test/unit/ContentstackClient-test.js
checksum: 974a4f335aef025b657d139bb290233a69bed1976b947c3c674e97baffe4ce2f
- filename: test/unit/ContentstackHTTPClient-test.js
checksum: 4043efd843e24da9afd0272c55ef4b0432e3374b2ca12b913f1a6654df3f62be
- filename: test/unit/contentstack-test.js
checksum: 2597efae3c1ab8cc173d5bf205f1c76932211f8e0eb2a16444e055d83481976c
# Sanity check test files - use process.env for all secrets (no hardcoded values)
- filename: test/sanity-check/api/environment-test.js
checksum: 91d76e6a2c4639db04071a30a9212df32777ab5f0e3a23dc101f4d62c13609b0
- filename: test/sanity-check/env.example.txt
checksum: 3339944cd20d6d72f70a92e54af3de96736250b4b7117a29577575f9b52ed611
- filename: test/sanity-check/api/token-test.js
checksum: 951d45bde20704529b38f628ba839a3c4f7a81ffe9d0a0593ff75b42632772db
- filename: test/sanity-check/api/webhook-test.js
checksum: 4928ae0eb72a47bced3b1a1eb18bc436141280bd41b74c54f03c1164911fd776
- filename: test/sanity-check/mock/configurations.js
checksum: 1506d750a9344843b3f8370aa322a814cfc0b3ac60fc94e55b691d2246335b5e
- filename: test/sanity-check/api/ungroupedVariants-test.js
checksum: 16a1460702efd0f9146687a2a1750768f55798bb31e0259f90a6810bcc4ab60a
- filename: test/sanity-check/mock/global-fields.js
checksum: fb89a4a5028066689de774ca2f990c25c8a3acc46c0c6b97fee410f491853cc1
- filename: test/sanity-check/utility/ContentstackClient.js
checksum: 96ff5412eed26f5a27621dd307c9463f793a3e8dd977fe1e5453da78507ac2f6
- filename: test/sanity-check/api/variantGroup-test.js
checksum: 3fc26eca704bc9ce4650056c81be45f3586d3c947a18dfec58fee4447de56360
- filename: test/sanity-check/api/workflow-test.js
checksum: 032a2b92eb0a7cc72976b597d53aee0beb04f965e36c056b3c7e3c60ad187108
- filename: test/sanity-check/api/variants-test.js
checksum: 6e1c1b0bada5799bf38443db537673f586c0c3dfd7800a8aec9d5a7fb966c58c
- filename: test/sanity-check/mock/content-types/index.js
checksum: ff47f74037e22f791e2d7c6afbaccf7857b26b51dd2e2361b5b4b70d36057b7f
- filename: test/sanity-check/sanity.js
checksum: 523725a12c93abdc1b89a1e7ef38021184e7d710f8719290923f835f8d615693
- filename: test/sanity-check/api/user-test.js
checksum: 01a2224a02f6a0e1cd5fb10e289a349a32a5cf3eb39b9e06787031fde5aa8aca
- filename: test/sanity-check/api/locale-test.js
checksum: 91f8db01791a57c18e925c5896cc1960cdb951e6787fff886c008e17c25d5dea
- filename: test/sanity-check/api/asset-test.js
checksum: 97f19206080fcd5042e3eaa25429e92eac697530de8825cb66533164b73d9164
- filename: test/sanity-check/api/label-test.js
checksum: bf11c1ec13e66d9251380ac8fe028d51a809ffa174afa9518dfb1f599372381d
- filename: test/sanity-check/mock/webhook-import.json
checksum: 3fb331e842d640a29663fcbd4feee8284f46600869b39ac45c1fedaa7cde4969
- filename: test/sanity-check/api/taxonomy-test.js
checksum: accd5b96fff87b6a9aaec7ca053e5546402b5d084417fdc70f7f2bc7a2b8a353
- filename: test/sanity-check/api/release-test.js
checksum: 863c0ef7d65cfd33f245deb636d537c131ad29233ebafd88c223e555c4f80b82
- filename: test/sanity-check/utility/testHelpers.js
checksum: 204d11d739947259a3303fbe1d92c296dd82975fa8dff67a438853a3828c27a3
- filename: test/sanity-check/api/auditlog-test.js
checksum: 9d325aaf73760359dd4194c52ad01203ed7f078230e45282e84aab2b53613095
- filename: test/sanity-check/api/team-test.js
checksum: e4b7a6824b89e634981651ad29161901377f23bb37d3653a389ac3dc4e7653c7
- filename: test/sanity-check/api/oauth-test.js
checksum: fd8a4fe7a644955ea6609813c655d8fca6bb3c7eeea4ae2c5ba99d30b1950172
- filename: test/sanity-check/api/branchAlias-test.js
checksum: 0b6cacee74d7636e84ce095198f0234d491b79ea20d3978a742a5495692bd61d
- filename: test/sanity-check/utility/testSetup.js
checksum: e906e6a93953826857fa701db7094330ef88e342e719f3446e17c823576c3377
- filename: test/sanity-check/api/branch-test.js
checksum: 49c8fd18c59d45e4335f766591711849722206bce34860efa8eced7172f44efa
- filename: test/sanity-check/api/stack-test.js
checksum: abcc3b1a7a6e52a553645bd7a7a38b287402604f6b61df51a69745cd2aa8a187
- filename: test/sanity-check/api/previewToken-test.js
checksum: 9efe3852336f1c5f961682ca21673514b2bd1334a040c5d56983074f41c6b8e0
- filename: test/sanity-check/api/role-test.js
checksum: cdfa2ae59443ed02f5463c0e84314a3d94c72f395694de883bc873cd6708cf87
- filename: test/sanity-check/api/terms-test.js
checksum: 8a54b4b6e27f03a461a7b6c12cec2b9fd4b931ccb6e41959a6cfedb3a2482ee8
- filename: test/sanity-check/utility/requestLogger.js
checksum: 2b5282cfff084765312e1543bad3f890bc5b47ef27456f0a4c2e50d098292e32
- filename: test/sanity-check/api/contentType-test.js
checksum: 4d5178998f9f3c27550c5bd21540e254e08f79616e8615e7256ba2175cb4c8e1
- filename: test/sanity-check/api/bulkOperation-test.js
checksum: 6281e14c7a10864c586e95139f47ae2ee5bb2322a2beaec166a1f6ece830431b
- filename: test/sanity-check/api/entry-test.js
checksum: 9dc16b404a98ff9fa2c164fad0182b291b9c338dd58558dc5ef8dd75cf18bc1f
- filename: test/sanity-check/api/entryVariants-test.js
checksum: 2089e9134dece33179b88747c6e82377f1fb4eb74583281df05dd0816a907782
- filename: test/sanity-check/api/extension-test.js
checksum: 5083af9c4009cc969f7949ce97f97ab2e5b5f40366ecfdd402f491a6246c5e6f
- filename: test/sanity-check/api/globalfield-test.js
checksum: 1ba486167f2485853d9574322c233d28fc566e02db44bb9831b70fb9afaf7631
- filename: test/sanity-check/mock/index.js
checksum: 6c0d8f6e7c85cd2fa5f0a20e8a49e94df0dde1b2c1d7e9c39e8c9c6c8b8d5e2f1
- filename: test/unit/concurrency-Queue-test.js
checksum: 186438f9eb9ba4e7fd7f335dbea2afbae9ae969b7ae3ab1b517ec7a1633d255e
checksum: fd5c327f4fa1b334fdb1a2d903ac0213752e7829f31f19667215aa186c3efbbf
version: "1.0"




6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [v1.27.5](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.5) (2026-02-11)
- Fix
- Concurrency queue: when response errors have no `config` (e.g. after network retries exhaust in some environments, or when plugins return a new error object), the SDK now rejects with a catchable Error instead of throwing an unhandled TypeError and crashing the process
- Hardened `responseHandler` to safely handle errors without `config` (e.g. plugin-replaced errors) by guarding `config.onComplete` and still running queue `shift()` so rejections remain catchable
- Added optional chaining for `error.config` reads in the retry path and unit tests for missing-config scenarios

## [v1.27.4](https://github.com/contentstack/contentstack-management-javascript/tree/v1.27.4) (2026-02-02)
- Fix
- Removed content-type header from the release delete method
Expand Down
51 changes: 36 additions & 15 deletions lib/core/concurrency-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
logFinalFailure(errorInfo, this.config.maxNetworkRetries)
// Final error message
const finalError = new Error(`Network request failed after ${this.config.maxNetworkRetries} retries: ${errorInfo.reason}`)
finalError.code = error.code
finalError.code = error && error.code
finalError.originalError = error
finalError.retryAttempts = attempt - 1
return Promise.reject(finalError)
Expand All @@ -181,6 +181,16 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
const delay = calculateNetworkRetryDelay(attempt)
logRetryAttempt(errorInfo, attempt, delay)

// Guard: retry failures (e.g. from nested retries) may not have config in some
// environments. Reject with a catchable error instead of throwing TypeError.
if (!error || !error.config) {
const finalError = new Error(`Network request failed after retries: ${errorInfo.reason}`)
finalError.code = error && error.code
finalError.originalError = error
finalError.retryAttempts = attempt - 1
return Promise.reject(finalError)
}

// Initialize retry count if not present
if (!error.config.networkRetryCount) {
error.config.networkRetryCount = 0
Expand All @@ -200,9 +210,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
safeAxiosRequest(requestConfig)
.then((response) => {
// On successful retry, call the original onComplete to properly clean up
if (error.config.onComplete) {
error.config.onComplete()
}
error?.config?.onComplete?.()
shift() // Process next queued request
resolve(response)
})
Expand All @@ -214,17 +222,13 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
.then(resolve)
.catch((finalError) => {
// On final failure, clean up the running queue
if (error.config.onComplete) {
error.config.onComplete()
}
error?.config?.onComplete?.()
shift() // Process next queued request
reject(finalError)
})
} else {
// On non-retryable error, clean up the running queue
if (error.config.onComplete) {
error.config.onComplete()
}
error?.config?.onComplete?.()
shift() // Process next queued request
reject(retryError)
}
Expand Down Expand Up @@ -429,9 +433,12 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
}
})
}
// Response interceptor used for
// Response interceptor used for success and for error path (Promise.reject(responseHandler(err))).
// When used with an error, err may lack config (e.g. plugin returns new error). Guard so we don't throw.
const responseHandler = (response) => {
response.config.onComplete()
if (response?.config?.onComplete) {
response.config.onComplete()
}
shift()
return response
}
Expand Down Expand Up @@ -461,13 +468,27 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
}

const responseErrorHandler = error => {
let networkError = error.config.retryCount
// Guard: Axios errors normally have config; missing config can occur when a retry
// fails in certain environments or when non-Axios errors propagate (e.g. timeouts).
// Reject with a catchable error instead of throwing TypeError and crashing the process.
if (!error || !error.config) {
const fallbackError = new Error(
error && typeof error.message === 'string'
? error.message
: 'Network request failed: error object missing request config'
)
fallbackError.code = error?.code
fallbackError.originalError = error
return Promise.reject(runPluginOnResponseForError(fallbackError))
}

let networkError = error?.config?.retryCount ?? 0
let retryErrorType = null

// First, check for transient network errors
const networkErrorInfo = isTransientNetworkError(error)
if (networkErrorInfo && this.config.retryOnNetworkFailure) {
const networkRetryCount = error.config.networkRetryCount || 0
const networkRetryCount = error?.config?.networkRetryCount || 0
return retryNetworkError(error, networkErrorInfo, networkRetryCount + 1)
}

Expand All @@ -482,7 +503,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
var response = error.response
if (!response) {
if (error.code === 'ECONNABORTED') {
const timeoutMs = error.config.timeout || this.config.timeout || 'unknown'
const timeoutMs = error?.config?.timeout || this.config.timeout || 'unknown'
error.response = {
...error.response,
status: 408,
Expand Down
2 changes: 1 addition & 1 deletion lib/organization/teams/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function Teams (http, data) {
* email: 'abc@abc.com'
* }
* ],
* organizationRole: 'blt09e5dfced326aaea',
* organizationRole: 'blt0000000000000000',
* stackRoleMapping: []
* }
* client.organization('organizationUid').teams('teamUid').update(updateData)
Expand Down
Loading
Loading