Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ example/**/*

dist/
dist/*
.DS_store
10 changes: 1 addition & 9 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
fileignoreconfig:
- filename: .github/workflows/secrets-scan.yml
ignore_detectors:
- filecontent
- filename: .github/workflows/check-version-bump.yml
ignore_detectors:
- filecontent
- filename: package-lock.json
checksum: 1525b038bc7500db7a4b489db28529cd0b2ada15afc7110d818fe13657303612
- filename: .husky/pre-commit
checksum: 1b9367d219802de2e3a8af9c5c698e0c255c00af89339d73bdbb8acf5275079f
checksum: 009fb6f2f26eda48369458fddb51a255ebbb4c73bd66d247d428c3a0faf2ec1b
version: ""
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ module.exports = {
// The test environment that will be used for testing
testEnvironment: 'node',

setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],

// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
testPathIgnorePatterns: [
Expand Down
10 changes: 10 additions & 0 deletions jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-env jest */
// marked ships ESM; Jest/ts-jest need a stub for tests that load the app entry.
jest.mock('marked', () => ({
__esModule: true,
default: {
parse: jest.fn(() => ''),
setOptions: jest.fn(),
use: jest.fn(),
},
}))
93 changes: 47 additions & 46 deletions package-lock.json

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

11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
{
"name": "@contentstack/datasync-manager",
"author": "Contentstack LLC <support@contentstack.com>",
"version": "2.4.0-beta.2",
"version": "2.4.0-beta.3",
"description": "The primary module of Contentstack DataSync. Syncs Contentstack data with your server using Contentstack Sync API",
"main": "dist/index.js",
"dependencies": {
"@braintree/sanitize-url": "^7.1.2",
"debug": "^4.4.3",
"dns-socket": "^4.2.2",
"lodash": "^4.18.1",
"marked": "^17.0.5",
"marked": "^17.0.6",
"write-file-atomic": "7.0.1"
},
"devDependencies": {
"@semantic-release/commit-analyzer": "^9.0.2",
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^12.0.1",
"@semantic-release/npm": "^12.0.2",
"@semantic-release/release-notes-generator": "^10.0.3",
"@types/debug": "0.0.31",
"@types/jest": "23.3.14",
"@types/lodash": "4.17.15",
"@types/lodash": "4.17.24",
"@types/marked": "^4.3.2",
"@types/mkdirp": "0.5.2",
"@types/nock": "9.3.1",
Expand Down Expand Up @@ -48,6 +48,9 @@
"start": "dist",
"tslint": "npx tslint -c tslint.json 'src/**/*.ts' --fix",
"test": "PLUGIN_PATH=./test/dummy jest --colors --coverage --verbose",
"mock:sync-api": "node scripts/sync-api-mock-server/server.js",
"mock:run-all-scenarios": "node scripts/sync-api-mock-server/run-all-scenarios.js",
"demo:mock": "node scripts/demo-with-mock.js",
"lint": "eslint",
"semantic-release": "semantic-release",
"husky-check": "npx husky && chmod +x .husky/pre-commit"
Expand Down
4 changes: 3 additions & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ export const get = (req, RETRY = 1) => {
httpRequest.setTimeout(options.timeout, () => {
debug(MESSAGES.API.REQUEST_TIMEOUT(options.path))
httpRequest.destroy()
reject(new Error('Request timeout'))
const timeoutError: any = new Error('Request timeout')
timeoutError.code = 'ETIMEDOUT'
Comment thread
cs-raj marked this conversation as resolved.
Outdated
reject(timeoutError)
})

// Enhanced error handling for network and connection errors
Expand Down
20 changes: 12 additions & 8 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,16 @@ const check = async () => {
const sync = async () => {
try {
debug(MESSAGES.SYNC_CORE.SYNC_STARTED);
const tokenObject = await getToken();
let tokenObject: IToken
try {
tokenObject = await getToken() as IToken
} catch (tokenError) {
// check() sets SQ before sync(); fire() is never entered — release the gate
flag.SQ = false
throw tokenError
}
debug(MESSAGES.SYNC_CORE.SYNC_TOKEN_OBJECT, tokenObject);
const token: IToken = (tokenObject as IToken)
const token: IToken = tokenObject
const request: any = {
qs: {
environment: process.env.SYNC_ENV || Contentstack.environment || 'development',
Expand Down Expand Up @@ -251,7 +258,7 @@ export const unlock = (refire?: boolean) => {
.catch(flag.requestCache.reject)
}
}
check()
return check()
}

/**
Expand Down Expand Up @@ -373,18 +380,15 @@ const fire = (req: IApiRequest) => {
if (parsedError.error_code === 141) {
logger.error(MESSAGES.SYNC_CORE.OUTDATED_SYNC_TOKEN)
logger.info(MESSAGES.SYNC_CORE.SYNC_TOKEN_RENEWAL)
// Reset flag so next webhook notification can trigger a fresh sync
flag.SQ = false
// Reset sync_token so next sync starts fresh with init=true
Contentstack.sync_token = undefined
}
} catch (parseError) {
// Not a JSON error or not Error 141, continue with normal handling
}

if (netConnectivityIssues(error)) {
flag.SQ = false
}
// Any failed sync API attempt must release the gate so the next notify/poke can run
flag.SQ = false

return reject(error)
})
Expand Down
21 changes: 17 additions & 4 deletions src/core/inet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,23 @@ export const checkNetConnectivity = () => {
}

export const netConnectivityIssues = (error) => {
// Include socket hang up and connection reset errors as network connectivity issues
const networkErrorCodes = ['ENOTFOUND', 'ETIMEDOUT', 'ECONNRESET', 'EPIPE', 'EHOSTUNREACH']

if (networkErrorCodes.includes(error.code) || error.message?.includes('socket hang up')) {
// Align with retryable codes in api.ts plus client-side timeout (no .code on Request timeout)
const networkErrorCodes = [
'ENOTFOUND',
'ETIMEDOUT',
'ECONNRESET',
'EPIPE',
'EHOSTUNREACH',
'ECONNREFUSED',
'ENETUNREACH',
'EAI_AGAIN',
]
const msg = typeof error?.message === 'string' ? error.message : ''

if (networkErrorCodes.includes(error?.code)) {
return true
}
if (msg.includes('socket hang up') || msg.includes('Request timeout')) {
return true
}

Expand Down
2 changes: 2 additions & 0 deletions test/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,5 @@ describe('test api - get()', () => {
// })

})

// Socket timeout + ETIMEDOUT: covered by test/core/inet.ts, scripts/sync-api-mock-server (MOCK_SCENARIO=hang).
3 changes: 2 additions & 1 deletion test/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('check lock-unlock', () => {
})
test('lock-unlock', () => {
expect(lock()).toBeUndefined()
expect(unlock(true)).toBeUndefined()
// unlock(true) would run check() -> sync() and requires init(); use unlock(false) to only exercise the gate
expect(unlock(false)).toBeUndefined()
})
})
Loading
Loading