@contentstack/datasync-manager 2.0.10 → 2.1.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/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/config.js CHANGED
@@ -129,4 +129,9 @@ exports.config = {
129
129
  saveFailedItems: true,
130
130
  saveFilteredItems: true,
131
131
  },
132
+ checkpoint: {
133
+ enabled: false,
134
+ filePath: ".checkpoint",
135
+ preserve: false // Set to true if you want to preserve the checkpoint file during clean operation
136
+ },
132
137
  };
@@ -1,9 +1,27 @@
1
1
  "use strict";
2
- /*!
3
- * Contentstack DataSync Manager
4
- * Copyright (c) 2019 Contentstack LLC
5
- * MIT Licensed
6
- */
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
7
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
8
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
9
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -18,6 +36,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
18
36
  };
19
37
  Object.defineProperty(exports, "__esModule", { value: true });
20
38
  exports.unlock = exports.lock = exports.poke = exports.pop = exports.unshift = exports.push = exports.init = void 0;
39
+ /*!
40
+ * Contentstack DataSync Manager
41
+ * Copyright (c) 2019 Contentstack LLC
42
+ * MIT Licensed
43
+ */
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
21
46
  const debug_1 = __importDefault(require("debug"));
22
47
  const events_1 = require("events");
23
48
  const lodash_1 = require("lodash");
@@ -30,6 +55,7 @@ const promise_map_1 = require("../util/promise.map");
30
55
  const inet_1 = require("./inet");
31
56
  const q_1 = require("./q");
32
57
  const token_management_1 = require("./token-management");
58
+ const helper_1 = require("../plugins/helper");
33
59
  const debug = (0, debug_1.default)('sync-core');
34
60
  const emitter = new events_1.EventEmitter();
35
61
  const formattedAssetType = '_assets';
@@ -55,6 +81,7 @@ const init = (contentStore, assetStore) => {
55
81
  return new Promise((resolve, reject) => {
56
82
  try {
57
83
  Contentstack = config.contentstack;
84
+ const checkPointConfig = config.checkpoint;
58
85
  const paths = config.paths;
59
86
  const environment = Contentstack.environment || process.env.NODE_ENV || 'development';
60
87
  debug(`Environment: ${environment}`);
@@ -64,6 +91,7 @@ const init = (contentStore, assetStore) => {
64
91
  limit: config.syncManager.limit,
65
92
  },
66
93
  };
94
+ loadCheckpoint(checkPointConfig, paths);
67
95
  if (typeof Contentstack.sync_token === 'string' && Contentstack.sync_token.length !== 0) {
68
96
  request.qs.sync_token = Contentstack.sync_token;
69
97
  }
@@ -94,6 +122,37 @@ const init = (contentStore, assetStore) => {
94
122
  });
95
123
  };
96
124
  exports.init = init;
125
+ const loadCheckpoint = (checkPointConfig, paths) => {
126
+ if (!(checkPointConfig === null || checkPointConfig === void 0 ? void 0 : checkPointConfig.enabled))
127
+ return;
128
+ // Try reading checkpoint from primary path
129
+ let checkpoint = readHiddenFile(paths.checkpoint);
130
+ // Fallback to filePath in config if not found
131
+ if (!checkpoint) {
132
+ const fallbackPath = path.join((0, helper_1.sanitizePath)(__dirname), (0, helper_1.sanitizePath)(checkPointConfig.filePath || ".checkpoint"));
133
+ checkpoint = readHiddenFile(fallbackPath);
134
+ }
135
+ // Set sync token if checkpoint is found
136
+ if (checkpoint) {
137
+ debug("Found sync token in checkpoint file:", checkpoint);
138
+ Contentstack.sync_token = checkpoint.token;
139
+ debug("Using sync token:", Contentstack.sync_token);
140
+ }
141
+ };
142
+ function readHiddenFile(filePath) {
143
+ try {
144
+ if (!fs.existsSync(filePath)) {
145
+ logger_1.logger.error("File does not exist:", filePath);
146
+ return;
147
+ }
148
+ const data = fs.readFileSync(filePath, "utf8");
149
+ return JSON.parse(data);
150
+ }
151
+ catch (err) {
152
+ logger_1.logger.error("Error reading file:", err);
153
+ return undefined;
154
+ }
155
+ }
97
156
  const push = (data) => {
98
157
  Q.emit('push', data);
99
158
  };
@@ -251,6 +310,7 @@ const fire = (req) => {
251
310
  });
252
311
  return (0, promise_map_1.map)(contentTypeUids, (uid) => {
253
312
  return new Promise((mapResolve, mapReject) => {
313
+ debug(`API called with for content type: ${uid}`);
254
314
  return (0, api_1.get)({
255
315
  path: `${Contentstack.apis.content_types}${uid}`,
256
316
  qs: {
@@ -271,6 +331,7 @@ const fire = (req) => {
271
331
  err.code = 'ICTC';
272
332
  return mapReject(err);
273
333
  }).catch((error) => {
334
+ debug('Error [map] fetching content type schema:', error);
274
335
  if ((0, inet_1.netConnectivityIssues)(error)) {
275
336
  flag.SQ = false;
276
337
  }
@@ -286,14 +347,17 @@ const fire = (req) => {
286
347
  flag.SQ = false;
287
348
  }
288
349
  // Errorred while fetching content type schema
350
+ debug('Error [mapResolve]:', error);
289
351
  return reject(error);
290
352
  });
291
353
  }).catch((processError) => {
354
+ debug('Error [filterItems]:', processError);
292
355
  return reject(processError);
293
356
  });
294
357
  }
295
358
  return postProcess(req, syncResponse)
296
- .then(resolve);
359
+ .then(resolve)
360
+ .catch(reject);
297
361
  }).catch((error) => {
298
362
  debug('Error [fire]', error);
299
363
  if ((0, inet_1.netConnectivityIssues)(error)) {
@@ -335,6 +399,7 @@ const postProcess = (req, resp) => {
335
399
  flag.SQ = false;
336
400
  return resolve('');
337
401
  }
402
+ debug(`Re-Fire called with: ${JSON.stringify(req)}`);
338
403
  return fire(req)
339
404
  .then(resolve)
340
405
  .catch(reject);
@@ -22,47 +22,53 @@ const pluginMethods = ['beforeSync', 'afterSync'];
22
22
  */
23
23
  const load = (config) => {
24
24
  debug('Plugins load called');
25
- const pluginInstances = {
26
- external: {},
27
- internal: {},
28
- };
29
- const plugins = config.plugins || [];
30
- pluginMethods.forEach((pluginMethod) => {
31
- pluginInstances.external[pluginMethod] = pluginInstances[pluginMethod] || [];
32
- pluginInstances.internal[pluginMethod] = pluginInstances[pluginMethod] || [];
33
- });
34
- plugins.forEach((plugin) => {
35
- (0, validations_1.validatePlugin)(plugin);
36
- const pluginName = plugin.name;
37
- const slicedName = pluginName.slice(0, 13);
38
- let isInternal = false;
39
- if (slicedName === '_cs_internal_') {
40
- isInternal = true;
41
- }
42
- const pluginPath = (0, index_1.normalizePluginPath)(config, plugin, isInternal);
43
- const Plugin = require(pluginPath);
44
- Plugin.options = plugin.options || {};
45
- // execute/initiate plugin
46
- Plugin();
25
+ try {
26
+ const pluginInstances = {
27
+ external: {},
28
+ internal: {},
29
+ };
30
+ const plugins = config.plugins || [];
47
31
  pluginMethods.forEach((pluginMethod) => {
48
- if ((0, lodash_1.hasIn)(Plugin, pluginMethod)) {
49
- if (plugin.disabled) {
50
- // do nothing
51
- }
52
- else if (isInternal) {
53
- pluginInstances.internal[pluginMethod].push(Plugin[pluginMethod]);
32
+ pluginInstances.external[pluginMethod] = pluginInstances[pluginMethod] || [];
33
+ pluginInstances.internal[pluginMethod] = pluginInstances[pluginMethod] || [];
34
+ });
35
+ plugins.forEach((plugin) => {
36
+ (0, validations_1.validatePlugin)(plugin);
37
+ const pluginName = plugin.name;
38
+ const slicedName = pluginName.slice(0, 13);
39
+ let isInternal = false;
40
+ if (slicedName === '_cs_internal_') {
41
+ isInternal = true;
42
+ }
43
+ const pluginPath = (0, index_1.normalizePluginPath)(config, plugin, isInternal);
44
+ const Plugin = require(pluginPath);
45
+ Plugin.options = plugin.options || {};
46
+ // execute/initiate plugin
47
+ Plugin();
48
+ pluginMethods.forEach((pluginMethod) => {
49
+ if ((0, lodash_1.hasIn)(Plugin, pluginMethod)) {
50
+ if (plugin.disabled) {
51
+ // do nothing
52
+ }
53
+ else if (isInternal) {
54
+ pluginInstances.internal[pluginMethod].push(Plugin[pluginMethod]);
55
+ }
56
+ else {
57
+ pluginInstances.external[pluginMethod].push(Plugin[pluginMethod]);
58
+ }
59
+ debug(`${pluginMethod} loaded from ${pluginName} successfully!`);
54
60
  }
55
61
  else {
56
- pluginInstances.external[pluginMethod].push(Plugin[pluginMethod]);
62
+ debug(`${pluginMethod} not found in ${pluginName}`);
57
63
  }
58
- debug(`${pluginMethod} loaded from ${pluginName} successfully!`);
59
- }
60
- else {
61
- debug(`${pluginMethod} not found in ${pluginName}`);
62
- }
64
+ });
63
65
  });
64
- });
65
- debug('Plugins loaded successfully!');
66
- return pluginInstances;
66
+ debug('Plugins loaded successfully!');
67
+ return pluginInstances;
68
+ }
69
+ catch (error) {
70
+ debug('Error while loading plugins:', error);
71
+ throw new Error(`Failed to load plugins: ${error === null || error === void 0 ? void 0 : error.message}`);
72
+ }
67
73
  };
68
74
  exports.load = load;
@@ -131,8 +131,9 @@ const formatItems = (items, config) => {
131
131
  items[i]._type = config.contentstack.actions.publish;
132
132
  // extra keys
133
133
  items[i]._synced_at = time;
134
- items[i].locale = items[i].data.publish_details.locale;
135
- items[i] = (0, lodash_1.merge)(items[i], items[i].data);
134
+ const assetLocale = items[i].data.publish_details.locale;
135
+ items[i] = (0, lodash_1.merge)((0, lodash_1.cloneDeep)(items[i]), items[i].data);
136
+ items[i].locale = assetLocale;
136
137
  break;
137
138
  case 'asset_unpublished':
138
139
  delete items[i].type;
@@ -152,8 +153,9 @@ const formatItems = (items, config) => {
152
153
  items[i]._content_type_uid = items[i].content_type_uid;
153
154
  // extra keys
154
155
  items[i]._synced_at = time;
155
- items[i].locale = items[i].data.publish_details.locale;
156
- items[i] = (0, lodash_1.merge)(items[i], items[i].data);
156
+ const entryLocale = items[i].data.publish_details.locale;
157
+ items[i] = (0, lodash_1.merge)((0, lodash_1.cloneDeep)(items[i]), items[i].data);
158
+ items[i].locale = entryLocale;
157
159
  break;
158
160
  case 'entry_unpublished':
159
161
  delete items[i].type;
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.0.10",
4
+ "version": "2.1.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": {