@contentstack/datasync-manager 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -154,6 +154,23 @@ And here's an example to get you started:
154
154
  }
155
155
  ```
156
156
 
157
+ ### Environment Variables
158
+
159
+ The following environment variables can be used to customize the behavior of Contentstack DataSync Manager:
160
+
161
+ | Variable | Description | Default |
162
+ |--------------|-----------------------------------------------------------------------------|------------------------|
163
+ | `TOKEN_PATH` | Path to the directory where token/checkpoint/ledger files are stored. | Project root directory |
164
+ | `PLUGIN_PATH`| Path to the directory where plugins are loaded from. | Project root directory |
165
+ | `NODE_ENV` | Node.js environment (affects config/environment selection). | `development` |
166
+ | `SYNC_ENV` | Overrides the environment used for sync operations. | Value of `NODE_ENV` |
167
+ | `KILLDURATION`| Time (in ms) before the process is forcefully killed (overrides config). | Value from config |
168
+
169
+ **Note:**
170
+ - `TOKEN_PATH` is especially useful for storing token data in a custom directory (e.g., for selective re-syncing based on timestamps).
171
+ - If a relative path is provided, it is resolved from the project root.
172
+ - These variables can be set in your shell or in your process manager configuration.
173
+
157
174
  ### Further Reading
158
175
 
159
176
  - [Getting started with Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync)
package/dist/api.js CHANGED
@@ -17,11 +17,13 @@ const sanitize_url_1 = require("@braintree/sanitize-url");
17
17
  const fs_1 = require("./util/fs");
18
18
  const debug = (0, debug_1.default)('api');
19
19
  let MAX_RETRY_LIMIT;
20
+ let RETRY_DELAY_BASE = 200; // Default base delay in milliseconds
21
+ let TIMEOUT = 30000; // Default timeout in milliseconds
20
22
  let Contentstack;
21
23
  /**
22
24
  * @description Initialize sync utilities API requests
23
25
  * @param {Object} contentstack - Contentstack configuration details
24
- */
26
+ */
25
27
  const init = (contentstack) => {
26
28
  const packageInfo = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', 'package.json')));
27
29
  Contentstack = contentstack;
@@ -36,6 +38,12 @@ const init = (contentstack) => {
36
38
  if (Contentstack.MAX_RETRY_LIMIT) {
37
39
  MAX_RETRY_LIMIT = Contentstack.MAX_RETRY_LIMIT;
38
40
  }
41
+ if (Contentstack.RETRY_DELAY_BASE) {
42
+ RETRY_DELAY_BASE = Contentstack.RETRY_DELAY_BASE;
43
+ }
44
+ if (Contentstack.TIMEOUT) {
45
+ TIMEOUT = Contentstack.TIMEOUT;
46
+ }
39
47
  };
40
48
  exports.init = init;
41
49
  /**
@@ -60,12 +68,13 @@ const get = (req, RETRY = 1) => {
60
68
  path: (0, sanitize_url_1.sanitizeUrl)(encodeURI(req.path)),
61
69
  port: Contentstack.port,
62
70
  protocol: Contentstack.protocol,
71
+ timeout: TIMEOUT, // Configurable timeout to prevent socket hang ups
63
72
  };
64
73
  try {
65
74
  debug(`${options.method.toUpperCase()}: ${options.path}`);
66
75
  let timeDelay;
67
76
  let body = '';
68
- (0, https_1.request)(options, (response) => {
77
+ const httpRequest = (0, https_1.request)(options, (response) => {
69
78
  response
70
79
  .setEncoding('utf-8')
71
80
  .on('data', (chunk) => body += chunk)
@@ -75,8 +84,8 @@ const get = (req, RETRY = 1) => {
75
84
  return resolve(JSON.parse(body));
76
85
  }
77
86
  else if (response.statusCode === 429) {
78
- timeDelay = Math.pow(Math.SQRT2, RETRY) * 200;
79
- debug(`API rate limit exceeded. Retrying ${options.path} with ${timeDelay} sec delay`);
87
+ timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
88
+ debug(`API rate limit exceeded. Retrying ${options.path} with ${timeDelay} ms delay`);
80
89
  return setTimeout(() => {
81
90
  return (0, exports.get)(req, RETRY)
82
91
  .then(resolve)
@@ -85,8 +94,8 @@ const get = (req, RETRY = 1) => {
85
94
  }
86
95
  else if (response.statusCode >= 500) {
87
96
  // retry, with delay
88
- timeDelay = Math.pow(Math.SQRT2, RETRY) * 200;
89
- debug(`Retrying ${options.path} with ${timeDelay} sec delay`);
97
+ timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
98
+ debug(`Retrying ${options.path} with ${timeDelay} ms delay`);
90
99
  RETRY++;
91
100
  return setTimeout(() => {
92
101
  return (0, exports.get)(req, RETRY)
@@ -99,9 +108,31 @@ const get = (req, RETRY = 1) => {
99
108
  return reject(body);
100
109
  }
101
110
  });
102
- })
103
- .on('error', reject)
104
- .end();
111
+ });
112
+ // Set socket timeout to handle socket hang ups
113
+ httpRequest.setTimeout(options.timeout, () => {
114
+ debug(`Request timeout for ${options.path || 'unknown'}`);
115
+ httpRequest.destroy();
116
+ reject(new Error('Request timeout'));
117
+ });
118
+ // Enhanced error handling for socket hang ups and connection resets
119
+ httpRequest.on('error', (error) => {
120
+ var _a;
121
+ debug(`Request error for ${options.path || 'unknown'}: ${(error === null || error === void 0 ? void 0 : error.message) || 'Unknown error'} (${(error === null || error === void 0 ? void 0 : error.code) || 'NO_CODE'})`);
122
+ // Handle socket hang up and connection reset errors with retry
123
+ if (((error === null || error === void 0 ? void 0 : error.code) === 'ECONNRESET' || ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('socket hang up'))) && RETRY <= MAX_RETRY_LIMIT) {
124
+ timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
125
+ debug(`Socket hang up detected. Retrying ${options.path || 'unknown'} with ${timeDelay} ms delay (attempt ${RETRY}/${MAX_RETRY_LIMIT})`);
126
+ RETRY++;
127
+ return setTimeout(() => {
128
+ return (0, exports.get)(req, RETRY)
129
+ .then(resolve)
130
+ .catch(reject);
131
+ }, timeDelay);
132
+ }
133
+ return reject(error);
134
+ });
135
+ httpRequest.end();
105
136
  }
106
137
  catch (error) {
107
138
  return reject(error);
package/dist/config.js CHANGED
@@ -35,6 +35,8 @@ exports.config = {
35
35
  },
36
36
  contentstack: {
37
37
  MAX_RETRY_LIMIT: 6,
38
+ TIMEOUT: 30000,
39
+ RETRY_DELAY_BASE: 200,
38
40
  actions: {
39
41
  delete: 'delete',
40
42
  publish: 'publish',
@@ -310,6 +310,7 @@ const fire = (req) => {
310
310
  });
311
311
  return (0, promise_map_1.map)(contentTypeUids, (uid) => {
312
312
  return new Promise((mapResolve, mapReject) => {
313
+ debug(`API called with for content type: ${uid}`);
313
314
  return (0, api_1.get)({
314
315
  path: `${Contentstack.apis.content_types}${uid}`,
315
316
  qs: {
@@ -330,6 +331,7 @@ const fire = (req) => {
330
331
  err.code = 'ICTC';
331
332
  return mapReject(err);
332
333
  }).catch((error) => {
334
+ debug('Error [map] fetching content type schema:', error);
333
335
  if ((0, inet_1.netConnectivityIssues)(error)) {
334
336
  flag.SQ = false;
335
337
  }
@@ -345,14 +347,17 @@ const fire = (req) => {
345
347
  flag.SQ = false;
346
348
  }
347
349
  // Errorred while fetching content type schema
350
+ debug('Error [mapResolve]:', error);
348
351
  return reject(error);
349
352
  });
350
353
  }).catch((processError) => {
354
+ debug('Error [filterItems]:', processError);
351
355
  return reject(processError);
352
356
  });
353
357
  }
354
358
  return postProcess(req, syncResponse)
355
- .then(resolve);
359
+ .then(resolve)
360
+ .catch(reject);
356
361
  }).catch((error) => {
357
362
  debug('Error [fire]', error);
358
363
  if ((0, inet_1.netConnectivityIssues)(error)) {
@@ -394,6 +399,7 @@ const postProcess = (req, resp) => {
394
399
  flag.SQ = false;
395
400
  return resolve('');
396
401
  }
402
+ debug(`Re-Fire called with: ${JSON.stringify(req)}`);
397
403
  return fire(req)
398
404
  .then(resolve)
399
405
  .catch(reject);
package/dist/core/inet.js CHANGED
@@ -68,7 +68,10 @@ const checkNetConnectivity = () => {
68
68
  };
69
69
  exports.checkNetConnectivity = checkNetConnectivity;
70
70
  const netConnectivityIssues = (error) => {
71
- if (error.code === 'ENOTFOUND' || error.code === 'ETIMEDOUT') {
71
+ var _a;
72
+ // Include socket hang up and connection reset errors as network connectivity issues
73
+ const networkErrorCodes = ['ENOTFOUND', 'ETIMEDOUT', 'ECONNRESET', 'EPIPE', 'EHOSTUNREACH'];
74
+ if (networkErrorCodes.includes(error.code) || ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('socket hang up'))) {
72
75
  return true;
73
76
  }
74
77
  return false;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contentstack/datasync-manager",
3
3
  "author": "Contentstack LLC <support@contentstack.com>",
4
- "version": "2.1.0",
4
+ "version": "2.1.2",
5
5
  "description": "The primary module of Contentstack DataSync. Syncs Contentstack data with your server using Contentstack Sync API",
6
6
  "main": "dist/index.js",
7
7
  "dependencies": {