@contentstack/datasync-manager 2.4.0-beta.0 → 2.4.0-beta.1

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/dist/api.js CHANGED
@@ -56,7 +56,7 @@ const messages_1 = require("./util/messages");
56
56
  const debug = (0, debug_1.default)('api');
57
57
  let MAX_RETRY_LIMIT;
58
58
  let RETRY_DELAY_BASE = 200; // Default base delay in milliseconds
59
- let TIMEOUT = 30000; // Default timeout in milliseconds
59
+ let TIMEOUT = 60000; // Increased from 30000 to 60000 (60 seconds) for large stack syncs
60
60
  let Contentstack;
61
61
  /**
62
62
  * @description Initialize sync utilities API requests
@@ -169,20 +169,24 @@ const get = (req, RETRY = 1) => {
169
169
  req.qs = {};
170
170
  }
171
171
  // Clear the invalid token parameters and reinitialize
172
- if (req.qs.sync_token) {
173
- delete req.qs.sync_token;
174
- }
175
- if (req.qs.pagination_token) {
176
- delete req.qs.pagination_token;
177
- }
172
+ delete req.qs.sync_token;
173
+ delete req.qs.pagination_token;
178
174
  req.qs.init = true;
175
+ // Reset req.path so it gets rebuilt from Contentstack.apis.sync
176
+ // (req.path has the old query string baked in from line 109)
177
+ delete req.path;
179
178
  // Mark this as a recovery attempt to prevent infinite loops
180
179
  if (!req._error141Recovery) {
181
180
  req._error141Recovery = true;
182
181
  debug('Retrying with init=true after Error 141');
183
- return (0, exports.get)(req, 1) // Reset retry counter for fresh start
184
- .then(resolve)
185
- .catch(reject);
182
+ // Use delayed retry
183
+ timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
184
+ debug(`Error 141 recovery: waiting ${timeDelay}ms before retry`);
185
+ return setTimeout(() => {
186
+ return (0, exports.get)(req, RETRY)
187
+ .then(resolve)
188
+ .catch(reject);
189
+ }, timeDelay);
186
190
  }
187
191
  else {
188
192
  debug('Error 141 recovery already attempted, failing to prevent infinite loop');
@@ -204,14 +208,28 @@ const get = (req, RETRY = 1) => {
204
208
  httpRequest.destroy();
205
209
  reject(new Error('Request timeout'));
206
210
  });
207
- // Enhanced error handling for socket hang ups and connection resets
211
+ // Enhanced error handling for network and connection errors
208
212
  httpRequest.on('error', (error) => {
209
- var _a;
213
+ var _a, _b;
210
214
  debug(messages_1.MESSAGES.API.REQUEST_ERROR(options.path, error === null || error === void 0 ? void 0 : error.message, error === null || error === void 0 ? void 0 : error.code));
211
- // Handle socket hang up and connection reset errors with retry
212
- 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) {
215
+ // List of retryable network error codes
216
+ const retryableErrors = [
217
+ 'ECONNRESET',
218
+ 'ETIMEDOUT',
219
+ 'ECONNREFUSED',
220
+ 'ENOTFOUND',
221
+ 'ENETUNREACH',
222
+ 'EAI_AGAIN',
223
+ 'EPIPE',
224
+ 'EHOSTUNREACH', // Host unreachable
225
+ ];
226
+ // Check if error is retryable
227
+ const isRetryable = retryableErrors.includes(error === null || error === void 0 ? void 0 : error.code) ||
228
+ ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('socket hang up')) ||
229
+ ((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes('ETIMEDOUT'));
230
+ if (isRetryable && RETRY <= MAX_RETRY_LIMIT) {
213
231
  timeDelay = Math.pow(Math.SQRT2, RETRY) * RETRY_DELAY_BASE;
214
- debug(messages_1.MESSAGES.API.SOCKET_HANGUP_RETRY(options.path, timeDelay, RETRY, MAX_RETRY_LIMIT));
232
+ debug(`Network error ${(error === null || error === void 0 ? void 0 : error.code) || (error === null || error === void 0 ? void 0 : error.message)}: waiting ${timeDelay}ms before retry ${RETRY}/${MAX_RETRY_LIMIT}`);
215
233
  RETRY++;
216
234
  return setTimeout(() => {
217
235
  return (0, exports.get)(req, RETRY)
@@ -42,7 +42,6 @@ exports.unlock = exports.lock = exports.poke = exports.pop = exports.unshift = e
42
42
  * MIT Licensed
43
43
  */
44
44
  const fs = __importStar(require("fs"));
45
- const path = __importStar(require("path"));
46
45
  const debug_1 = __importDefault(require("debug"));
47
46
  const events_1 = require("events");
48
47
  const lodash_1 = require("lodash");
@@ -56,7 +55,6 @@ const promise_map_1 = require("../util/promise.map");
56
55
  const inet_1 = require("./inet");
57
56
  const q_1 = require("./q");
58
57
  const token_management_1 = require("./token-management");
59
- const helper_1 = require("../plugins/helper");
60
58
  const debug = (0, debug_1.default)('sync-core');
61
59
  const emitter = new events_1.EventEmitter();
62
60
  const formattedAssetType = '_assets';
@@ -126,13 +124,8 @@ exports.init = init;
126
124
  const loadCheckpoint = (checkPointConfig, paths) => {
127
125
  if (!(checkPointConfig === null || checkPointConfig === void 0 ? void 0 : checkPointConfig.enabled))
128
126
  return;
129
- // Try reading checkpoint from primary path
127
+ // Read checkpoint from configured path only
130
128
  let checkpoint = readHiddenFile(paths.checkpoint);
131
- // Fallback to filePath in config if not found
132
- if (!checkpoint) {
133
- const fallbackPath = path.join((0, helper_1.sanitizePath)(__dirname), (0, helper_1.sanitizePath)(checkPointConfig.filePath || ".checkpoint"));
134
- checkpoint = readHiddenFile(fallbackPath);
135
- }
136
129
  // Set sync token if checkpoint is found
137
130
  if (checkpoint) {
138
131
  debug(messages_1.MESSAGES.SYNC_CORE.TOKEN_FOUND, checkpoint);
@@ -361,15 +354,17 @@ const fire = (req) => {
361
354
  .catch(reject);
362
355
  }).catch((error) => {
363
356
  debug(messages_1.MESSAGES.SYNC_CORE.ERROR_FIRE, error);
364
- // Check if this is an Error 141 (invalid token) - enhanced handling
357
+ // Check if this is an Error 141 (outdated token)
358
+ // Note: api.ts already handles recovery by retrying with init=true
365
359
  try {
366
360
  const parsedError = typeof error === 'string' ? JSON.parse(error) : error;
367
361
  if (parsedError.error_code === 141) {
368
- logger_1.logger.error('Error 141: Invalid sync_token detected. Token has been reset.');
369
- logger_1.logger.info('System will automatically re-initialize with fresh token on next sync.');
370
- // The error has already been handled in api.ts with init=true
371
- // Just ensure we don't keep retrying with the bad token
362
+ logger_1.logger.error(messages_1.MESSAGES.SYNC_CORE.OUTDATED_SYNC_TOKEN);
363
+ logger_1.logger.info(messages_1.MESSAGES.SYNC_CORE.SYNC_TOKEN_RENEWAL);
364
+ // Reset flag so next webhook notification can trigger a fresh sync
372
365
  flag.SQ = false;
366
+ // Reset sync_token so next sync starts fresh with init=true
367
+ Contentstack.sync_token = undefined;
373
368
  }
374
369
  }
375
370
  catch (parseError) {
@@ -378,7 +373,6 @@ const fire = (req) => {
378
373
  if ((0, inet_1.netConnectivityIssues)(error)) {
379
374
  flag.SQ = false;
380
375
  }
381
- // do something
382
376
  return reject(error);
383
377
  });
384
378
  });
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  const { cloneDeep } = require('lodash');
3
- const { getConfig } = require('../index');
4
3
  const fieldType = {
5
4
  REFERENCE: 'reference',
6
5
  GLOBAL_FIELD: 'global_field',
@@ -186,6 +185,9 @@ const checkReferences = (schema, key) => {
186
185
  }
187
186
  };
188
187
  exports.buildAssetObject = (asset, locale, entry_uid, content_type_uid) => {
188
+ // Lazy-load getConfig at runtime to avoid circular dependency
189
+ // (helper.js is loaded during plugin init before index.ts finishes exporting)
190
+ const { getConfig } = require('../index');
189
191
  const { contentstack } = getConfig();
190
192
  // add locale key to inside of asset
191
193
  asset.locale = locale;
@@ -81,6 +81,8 @@ exports.MESSAGES = {
81
81
  ERROR_FIRE: 'Error during fire operation.',
82
82
  REFIRE_CALLED: (req) => `Re-fire operation triggered with: ${JSON.stringify(req)}`,
83
83
  CHECKPOINT_LOCKDOWN: 'Checkpoint: lockdown has been invoked',
84
+ OUTDATED_SYNC_TOKEN: 'Sync token is outdated and no longer accepted by the API. Recovering...',
85
+ SYNC_TOKEN_RENEWAL: 'Renewing sync token. This typically happens after network interruptions or long inactivity.',
84
86
  },
85
87
  // Main index messages (index.ts)
86
88
  INDEX: {
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.4.0-beta.0",
4
+ "version": "2.4.0-beta.1",
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": {