@contentstack/cli-cm-export 1.20.2 → 2.0.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.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/lib/commands/cm/stacks/export.js +15 -6
  3. package/lib/config/index.js +0 -2
  4. package/lib/export/module-exporter.js +37 -38
  5. package/lib/export/modules/assets.js +71 -14
  6. package/lib/export/modules/base-class.d.ts +17 -0
  7. package/lib/export/modules/base-class.js +45 -0
  8. package/lib/export/modules/content-types.js +30 -8
  9. package/lib/export/modules/custom-roles.js +61 -21
  10. package/lib/export/modules/entries.d.ts +2 -0
  11. package/lib/export/modules/entries.js +137 -37
  12. package/lib/export/modules/environments.js +45 -20
  13. package/lib/export/modules/extensions.js +42 -17
  14. package/lib/export/modules/global-fields.d.ts +1 -1
  15. package/lib/export/modules/global-fields.js +24 -6
  16. package/lib/export/modules/index.d.ts +1 -0
  17. package/lib/export/modules/index.js +1 -0
  18. package/lib/export/modules/labels.js +43 -16
  19. package/lib/export/modules/locales.js +31 -12
  20. package/lib/export/modules/marketplace-apps.d.ts +5 -2
  21. package/lib/export/modules/marketplace-apps.js +95 -25
  22. package/lib/export/modules/personalize.d.ts +12 -2
  23. package/lib/export/modules/personalize.js +181 -46
  24. package/lib/export/modules/stack.js +83 -28
  25. package/lib/export/modules/taxonomies.d.ts +4 -9
  26. package/lib/export/modules/taxonomies.js +123 -75
  27. package/lib/export/modules/webhooks.js +40 -17
  28. package/lib/export/modules/workflows.js +57 -20
  29. package/lib/types/default-config.d.ts +0 -2
  30. package/lib/types/index.d.ts +1 -1
  31. package/lib/utils/common-helper.d.ts +1 -2
  32. package/lib/utils/common-helper.js +1 -12
  33. package/lib/utils/constants.d.ts +147 -0
  34. package/lib/utils/constants.js +160 -0
  35. package/lib/utils/export-config-handler.js +2 -0
  36. package/lib/utils/index.d.ts +1 -0
  37. package/lib/utils/index.js +6 -1
  38. package/lib/utils/marketplace-app-helper.d.ts +1 -0
  39. package/lib/utils/marketplace-app-helper.js +21 -12
  40. package/lib/utils/progress-strategy-registry.d.ts +7 -0
  41. package/lib/utils/progress-strategy-registry.js +98 -0
  42. package/messages/index.json +4 -2
  43. package/oclif.manifest.json +1 -1
  44. package/package.json +3 -3
  45. package/lib/export/modules-js/assets.d.ts +0 -43
  46. package/lib/export/modules-js/assets.js +0 -396
  47. package/lib/export/modules-js/content-types.d.ts +0 -21
  48. package/lib/export/modules-js/content-types.js +0 -76
  49. package/lib/export/modules-js/custom-roles.d.ts +0 -21
  50. package/lib/export/modules-js/custom-roles.js +0 -76
  51. package/lib/export/modules-js/entries.d.ts +0 -18
  52. package/lib/export/modules-js/entries.js +0 -143
  53. package/lib/export/modules-js/environments.d.ts +0 -16
  54. package/lib/export/modules-js/environments.js +0 -62
  55. package/lib/export/modules-js/extensions.d.ts +0 -18
  56. package/lib/export/modules-js/extensions.js +0 -57
  57. package/lib/export/modules-js/global-fields.d.ts +0 -22
  58. package/lib/export/modules-js/global-fields.js +0 -108
  59. package/lib/export/modules-js/index.d.ts +0 -2
  60. package/lib/export/modules-js/index.js +0 -31
  61. package/lib/export/modules-js/labels.d.ts +0 -14
  62. package/lib/export/modules-js/labels.js +0 -56
  63. package/lib/export/modules-js/locales.d.ts +0 -23
  64. package/lib/export/modules-js/locales.js +0 -68
  65. package/lib/export/modules-js/marketplace-apps.d.ts +0 -21
  66. package/lib/export/modules-js/marketplace-apps.js +0 -132
  67. package/lib/export/modules-js/stack.d.ts +0 -18
  68. package/lib/export/modules-js/stack.js +0 -91
  69. package/lib/export/modules-js/webhooks.d.ts +0 -18
  70. package/lib/export/modules-js/webhooks.js +0 -60
  71. package/lib/export/modules-js/workflows.d.ts +0 -16
  72. package/lib/export/modules-js/workflows.js +0 -89
package/README.md CHANGED
@@ -48,7 +48,7 @@ $ npm install -g @contentstack/cli-cm-export
48
48
  $ csdx COMMAND
49
49
  running command...
50
50
  $ csdx (--version)
51
- @contentstack/cli-cm-export/1.20.2 linux-x64 node-v22.19.0
51
+ @contentstack/cli-cm-export/2.0.0-beta.1 linux-x64 node-v22.20.0
52
52
  $ csdx --help [COMMAND]
53
53
  USAGE
54
54
  $ csdx COMMAND
@@ -6,7 +6,7 @@ const export_1 = require("../../../export");
6
6
  const utils_1 = require("../../../utils");
7
7
  class ExportCommand extends cli_command_1.Command {
8
8
  async run() {
9
- var _a;
9
+ var _a, _b;
10
10
  let exportDir = (0, cli_utilities_1.pathValidator)('logs');
11
11
  try {
12
12
  const { flags } = await this.parse(ExportCommand);
@@ -21,16 +21,26 @@ class ExportCommand extends cli_command_1.Command {
21
21
  const managementAPIClient = await (0, cli_utilities_1.managementSDKClient)(exportConfig);
22
22
  const moduleExporter = new export_1.ModuleExporter(managementAPIClient, exportConfig);
23
23
  await moduleExporter.start();
24
- if (!((_a = exportConfig.branches) === null || _a === void 0 ? void 0 : _a.length)) {
25
- (0, utils_1.writeExportMetaFile)(exportConfig);
26
- }
27
24
  cli_utilities_1.log.success(`The content of the stack ${exportConfig.apiKey} has been exported successfully!`, exportConfig.context);
28
25
  cli_utilities_1.log.info(`The exported content has been stored at '${exportDir}'`, exportConfig.context);
29
26
  cli_utilities_1.log.success(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`, exportConfig.context);
27
+ // Print comprehensive summary at the end
28
+ if (!exportConfig.branches)
29
+ cli_utilities_1.CLIProgressManager.printGlobalSummary();
30
+ if (!((_a = cli_utilities_1.configHandler.get('log')) === null || _a === void 0 ? void 0 : _a.showConsoleLogs)) {
31
+ cli_utilities_1.cliux.print(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`, { color: 'green' });
32
+ }
33
+ // Clear progress module setting now that export is complete
34
+ (0, cli_utilities_1.clearProgressModuleSetting)();
30
35
  }
31
36
  catch (error) {
37
+ // Clear progress module setting even on error
38
+ (0, cli_utilities_1.clearProgressModuleSetting)();
32
39
  (0, cli_utilities_1.handleAndLogError)(error);
33
- cli_utilities_1.log.info(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`);
40
+ if (!((_b = cli_utilities_1.configHandler.get('log')) === null || _b === void 0 ? void 0 : _b.showConsoleLogs)) {
41
+ cli_utilities_1.cliux.print(`Error: ${error}`, { color: 'red' });
42
+ cli_utilities_1.cliux.print(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`, { color: 'green' });
43
+ }
34
44
  }
35
45
  }
36
46
  // Create export context object
@@ -40,7 +50,6 @@ class ExportCommand extends cli_command_1.Command {
40
50
  command: ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.command) || 'cm:stacks:export',
41
51
  module: '',
42
52
  userId: cli_utilities_1.configHandler.get('userUid') || '',
43
- email: cli_utilities_1.configHandler.get('email') || '',
44
53
  sessionId: ((_c = this.context) === null || _c === void 0 ? void 0 : _c.sessionId) || '',
45
54
  apiKey: apiKey || '',
46
55
  orgId: cli_utilities_1.configHandler.get('oauthOrgUid') || '',
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const config = {
4
- contentVersion: 2,
5
4
  versioning: false,
6
5
  host: 'https://api.contentstack.io/v3',
7
6
  developerHubUrls: {
@@ -489,6 +488,5 @@ const config = {
489
488
  writeConcurrency: 5,
490
489
  developerHubBaseUrl: '',
491
490
  marketplaceAppEncryptionKey: 'nF2ejRQcTv',
492
- onlyTSModules: ['taxonomies'],
493
491
  };
494
492
  exports.default = config;
@@ -3,9 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const path = tslib_1.__importStar(require("path"));
5
5
  const cli_utilities_1 = require("@contentstack/cli-utilities");
6
- const utils_1 = require("../utils");
7
6
  const modules_1 = tslib_1.__importDefault(require("./modules"));
8
- const modules_js_1 = tslib_1.__importDefault(require("./modules-js"));
7
+ const utils_1 = require("../utils");
9
8
  class ModuleExporter {
10
9
  constructor(managementAPIClient, exportConfig) {
11
10
  this.managementAPIClient = managementAPIClient;
@@ -28,6 +27,8 @@ class ModuleExporter {
28
27
  this.exportConfig.branchEnabled = true;
29
28
  return this.exportByBranches();
30
29
  }
30
+ // If branches disabled then initialize the global summary
31
+ cli_utilities_1.CLIProgressManager.initializeGlobalSummary('EXPORT', this.exportConfig.branchName, 'Exporting content...');
31
32
  return this.export();
32
33
  }
33
34
  catch (error) {
@@ -35,25 +36,40 @@ class ModuleExporter {
35
36
  }
36
37
  }
37
38
  async exportByBranches() {
38
- // loop through the branches and export it parallel
39
- for (const branch of this.exportConfig.branches) {
40
- try {
41
- this.exportConfig.branchName = branch.uid;
42
- this.stackAPIClient.stackHeaders.branch = branch.uid;
43
- this.exportConfig.branchDir = path.join(this.exportConfig.exportDir, branch.uid);
44
- cli_utilities_1.log.info(`Exporting content from branch ${branch.uid}`, this.exportConfig.context);
45
- (0, utils_1.writeExportMetaFile)(this.exportConfig, this.exportConfig.branchDir);
46
- await this.export();
47
- cli_utilities_1.log.success(`The content of branch ${branch.uid} has been exported successfully!`, this.exportConfig.context);
39
+ let targetBranch;
40
+ if (this.exportConfig.branchName) {
41
+ // User specified a branch - export only that branch
42
+ targetBranch = this.exportConfig.branches.find((branch) => branch.uid === this.exportConfig.branchName);
43
+ if (!targetBranch) {
44
+ throw new Error(`Branch '${this.exportConfig.branchName}' not found in available branches`);
48
45
  }
49
- catch (error) {
50
- (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.exportConfig.context), { branch: branch.uid }), cli_utilities_1.messageHandler.parse('FAILED_EXPORT_CONTENT_BRANCH', { branch: branch.uid }));
51
- throw new Error(cli_utilities_1.messageHandler.parse('FAILED_EXPORT_CONTENT_BRANCH', { branch: branch.uid }));
46
+ }
47
+ else {
48
+ // No specific branch mentioned - export only the main branch
49
+ targetBranch = this.exportConfig.branches.find((branch) => branch.uid === 'main');
50
+ if (!targetBranch) {
51
+ throw new Error('No main branch or available branches found');
52
52
  }
53
53
  }
54
+ try {
55
+ this.exportConfig.branchName = targetBranch.uid;
56
+ this.stackAPIClient.stackHeaders.branch = targetBranch.uid;
57
+ this.exportConfig.branchDir = path.join(this.exportConfig.exportDir, targetBranch.uid);
58
+ // Initialize progress manager for the target branch
59
+ cli_utilities_1.CLIProgressManager.clearGlobalSummary();
60
+ cli_utilities_1.CLIProgressManager.initializeGlobalSummary(`EXPORT-${targetBranch.uid}`, targetBranch.uid, `Exporting "${targetBranch.uid}" branch content...`);
61
+ cli_utilities_1.log.info(`Exporting content from '${targetBranch.uid}' branch`, this.exportConfig.context);
62
+ await this.export();
63
+ cli_utilities_1.CLIProgressManager.printGlobalSummary();
64
+ cli_utilities_1.log.success(`The content of branch ${targetBranch.uid} has been exported successfully!`, this.exportConfig.context);
65
+ }
66
+ catch (error) {
67
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.exportConfig.context), { branch: targetBranch === null || targetBranch === void 0 ? void 0 : targetBranch.uid }), cli_utilities_1.messageHandler.parse('FAILED_EXPORT_CONTENT_BRANCH', { branch: targetBranch === null || targetBranch === void 0 ? void 0 : targetBranch.uid }));
68
+ throw new Error(cli_utilities_1.messageHandler.parse('FAILED_EXPORT_CONTENT_BRANCH', { branch: targetBranch === null || targetBranch === void 0 ? void 0 : targetBranch.uid }));
69
+ }
54
70
  }
55
71
  async export() {
56
- cli_utilities_1.log.info(`Started to export content, version is ${this.exportConfig.contentVersion}`, this.exportConfig.context);
72
+ cli_utilities_1.log.info(`Started to export content`, this.exportConfig.context);
57
73
  // checks for single module or all modules
58
74
  if (this.exportConfig.singleModuleExport) {
59
75
  return this.exportSingleModule(this.exportConfig.moduleName);
@@ -64,28 +80,11 @@ class ModuleExporter {
64
80
  cli_utilities_1.log.info(`Exporting module: ${moduleName}`, this.exportConfig.context);
65
81
  // export the modules by name
66
82
  // calls the module runner which inturn calls the module itself
67
- let exportedModuleResponse;
68
- if (this.exportConfig.contentVersion === 2) {
69
- exportedModuleResponse = await (0, modules_1.default)({
70
- stackAPIClient: this.stackAPIClient,
71
- exportConfig: this.exportConfig,
72
- moduleName,
73
- });
74
- }
75
- else {
76
- //NOTE - new modules support only ts
77
- if (this.exportConfig.onlyTSModules.indexOf(moduleName) === -1) {
78
- exportedModuleResponse = await (0, modules_js_1.default)({
79
- stackAPIClient: this.stackAPIClient,
80
- exportConfig: this.exportConfig,
81
- moduleName,
82
- });
83
- }
84
- }
85
- // set master locale to config
86
- if (moduleName === 'stack' && (exportedModuleResponse === null || exportedModuleResponse === void 0 ? void 0 : exportedModuleResponse.code)) {
87
- this.exportConfig.master_locale = { code: exportedModuleResponse.code };
88
- }
83
+ await (0, modules_1.default)({
84
+ stackAPIClient: this.stackAPIClient,
85
+ exportConfig: this.exportConfig,
86
+ moduleName,
87
+ });
89
88
  }
90
89
  async exportSingleModule(moduleName) {
91
90
  // Note stack is always exported
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const map_1 = tslib_1.__importDefault(require("lodash/map"));
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
5
6
  const chunk_1 = tslib_1.__importDefault(require("lodash/chunk"));
6
7
  const first_1 = tslib_1.__importDefault(require("lodash/first"));
7
8
  const merge_1 = tslib_1.__importDefault(require("lodash/merge"));
@@ -17,13 +18,15 @@ const node_path_1 = require("node:path");
17
18
  const cli_utilities_1 = require("@contentstack/cli-utilities");
18
19
  const config_1 = tslib_1.__importDefault(require("../../config"));
19
20
  const base_class_1 = tslib_1.__importDefault(require("./base-class"));
21
+ const utils_1 = require("../../utils");
20
22
  class ExportAssets extends base_class_1.default {
21
23
  constructor({ exportConfig, stackAPIClient }) {
22
24
  super({ exportConfig, stackAPIClient });
23
25
  this.assetConfig = config_1.default.modules.assets;
24
26
  this.assetsFolder = [];
25
27
  this.versionedAssets = [];
26
- this.exportConfig.context.module = 'assets';
28
+ this.exportConfig.context.module = utils_1.MODULE_CONTEXTS.ASSETS;
29
+ this.currentModuleName = utils_1.MODULE_NAMES[utils_1.MODULE_CONTEXTS.ASSETS];
27
30
  }
28
31
  get commonQueryParam() {
29
32
  return {
@@ -37,19 +40,55 @@ class ExportAssets extends base_class_1.default {
37
40
  cli_utilities_1.log.debug(`Assets root path resolved to: ${this.assetsRootPath}`, this.exportConfig.context);
38
41
  cli_utilities_1.log.debug('Fetching assets and folders count...', this.exportConfig.context);
39
42
  // NOTE step 1: Get assets and it's folder count in parallel
40
- const [assetsCount, assetsFolderCount] = await Promise.all([this.getAssetsCount(), this.getAssetsCount(true)]);
41
- cli_utilities_1.log.debug('Fetching assets and folders data...', this.exportConfig.context);
42
- // NOTE step 2: Get assets and it's folder data in parallel
43
- await Promise.all([this.getAssetsFolders(assetsFolderCount), this.getAssets(assetsCount)]);
44
- // NOTE step 3: Get versioned assets
45
- if (!(0, isEmpty_1.default)(this.versionedAssets) && this.assetConfig.includeVersionedAssets) {
46
- cli_utilities_1.log.debug('Fetching versioned assets metadata...', this.exportConfig.context);
47
- await this.getVersionedAssets();
43
+ const [assetsCount, assetsFolderCount] = await this.withLoadingSpinner(`${chalk_1.default.bold('ASSETS')}: Analyzing stack content...`, () => Promise.all([this.getAssetsCount(), this.getAssetsCount(true)]));
44
+ // Create nested progress manager
45
+ const progress = this.createNestedProgress(this.currentModuleName);
46
+ // Add sub-processes
47
+ if (typeof assetsFolderCount === 'number' && assetsFolderCount > 0) {
48
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_FOLDERS, assetsFolderCount);
49
+ }
50
+ if (typeof assetsCount === 'number' && assetsCount > 0) {
51
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_METADATA, assetsCount);
52
+ progress.addProcess(utils_1.PROCESS_NAMES.ASSET_DOWNLOADS, assetsCount);
53
+ }
54
+ try {
55
+ // Process asset folders
56
+ if (typeof assetsFolderCount === 'number' && assetsFolderCount > 0) {
57
+ progress
58
+ .startProcess(utils_1.PROCESS_NAMES.ASSET_FOLDERS)
59
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_FOLDERS].FETCHING, utils_1.PROCESS_NAMES.ASSET_FOLDERS);
60
+ await this.getAssetsFolders(assetsFolderCount);
61
+ progress.completeProcess(utils_1.PROCESS_NAMES.ASSET_FOLDERS, true);
62
+ }
63
+ // Process asset metadata
64
+ if (typeof assetsCount === 'number' && assetsCount > 0) {
65
+ progress
66
+ .startProcess(utils_1.PROCESS_NAMES.ASSET_METADATA)
67
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_METADATA].FETCHING, utils_1.PROCESS_NAMES.ASSET_METADATA);
68
+ await this.getAssets(assetsCount);
69
+ progress.completeProcess(utils_1.PROCESS_NAMES.ASSET_METADATA, true);
70
+ }
71
+ // Get versioned assets
72
+ if (!(0, isEmpty_1.default)(this.versionedAssets) && this.assetConfig.includeVersionedAssets) {
73
+ cli_utilities_1.log.debug('Fetching versioned assets metadata...', this.exportConfig.context);
74
+ progress.updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_METADATA].FETCHING_VERSION, utils_1.PROCESS_NAMES.ASSET_METADATA);
75
+ await this.getVersionedAssets();
76
+ }
77
+ // Download all assets
78
+ if (typeof assetsCount === 'number' && assetsCount > 0) {
79
+ progress
80
+ .startProcess(utils_1.PROCESS_NAMES.ASSET_DOWNLOADS)
81
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_DOWNLOADS].DOWNLOADING, utils_1.PROCESS_NAMES.ASSET_DOWNLOADS);
82
+ cli_utilities_1.log.debug('Starting download of all assets...', this.exportConfig.context);
83
+ await this.downloadAssets();
84
+ progress.completeProcess(utils_1.PROCESS_NAMES.ASSET_DOWNLOADS, true);
85
+ }
86
+ this.completeProgress(true);
87
+ cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('ASSET_EXPORT_COMPLETE'), this.exportConfig.context);
88
+ }
89
+ catch (error) {
90
+ this.completeProgress(false, (error === null || error === void 0 ? void 0 : error.message) || 'Asset export failed');
48
91
  }
49
- cli_utilities_1.log.debug('Starting download of all assets...', this.exportConfig.context);
50
- // NOTE step 4: Download all assets
51
- await this.downloadAssets();
52
- cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('ASSET_EXPORT_COMPLETE'), this.exportConfig.context);
53
92
  }
54
93
  /**
55
94
  * @method getAssetsFolders
@@ -63,10 +102,17 @@ class ExportAssets extends base_class_1.default {
63
102
  cli_utilities_1.log.debug(`Fetching asset folders with query: ${JSON.stringify(queryParam)}`, this.exportConfig.context);
64
103
  const onSuccess = ({ response: { items } }) => {
65
104
  cli_utilities_1.log.debug(`Fetched ${(items === null || items === void 0 ? void 0 : items.length) || 0} asset folders`, this.exportConfig.context);
66
- if (!(0, isEmpty_1.default)(items))
105
+ if (!(0, isEmpty_1.default)(items)) {
67
106
  this.assetsFolder.push(...items);
107
+ items.forEach((folder) => {
108
+ var _a;
109
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `folder: ${folder.name || folder.uid}`, null, utils_1.PROCESS_NAMES.ASSET_FOLDERS);
110
+ });
111
+ }
68
112
  };
69
113
  const onReject = ({ error }) => {
114
+ var _a;
115
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(false, 'asset folder', (error === null || error === void 0 ? void 0 : error.message) || utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_FOLDERS].FAILED, utils_1.PROCESS_NAMES.ASSET_FOLDERS);
70
116
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.exportConfig.context));
71
117
  };
72
118
  return this.makeConcurrentCall({
@@ -114,6 +160,8 @@ class ExportAssets extends base_class_1.default {
114
160
  metaHandler = customHandler;
115
161
  }
116
162
  const onReject = ({ error }) => {
163
+ var _a;
164
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(false, 'asset', (error === null || error === void 0 ? void 0 : error.message) || utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.ASSET_METADATA].FAILED, utils_1.PROCESS_NAMES.ASSET_METADATA);
117
165
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.exportConfig.context), cli_utilities_1.messageHandler.parse('ASSET_QUERY_FAILED'));
118
166
  };
119
167
  const onSuccess = ({ response: { items } }) => {
@@ -132,6 +180,11 @@ class ExportAssets extends base_class_1.default {
132
180
  if (!(0, isEmpty_1.default)(items)) {
133
181
  cli_utilities_1.log.debug(`Writing ${items.length} assets into file`, this.exportConfig.context);
134
182
  fs === null || fs === void 0 ? void 0 : fs.writeIntoFile(items, { mapKeyVal: true });
183
+ // Track progress for each asset with process name
184
+ items.forEach((asset) => {
185
+ var _a;
186
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `asset: ${asset.filename || asset.uid}`, null, utils_1.PROCESS_NAMES.ASSET_METADATA);
187
+ });
135
188
  }
136
189
  };
137
190
  return this.makeConcurrentCall({
@@ -252,6 +305,7 @@ class ExportAssets extends base_class_1.default {
252
305
  const apiBatches = (0, chunk_1.default)(listOfAssets, this.assetConfig.downloadLimit);
253
306
  const downloadedAssetsDirs = await (0, cli_utilities_1.getDirectories)((0, node_path_1.resolve)(this.assetsRootPath, 'files'));
254
307
  const onSuccess = ({ response: { data }, additionalInfo }) => {
308
+ var _a;
255
309
  const { asset } = additionalInfo;
256
310
  const assetFolderPath = (0, node_path_1.resolve)(this.assetsRootPath, 'files', asset.uid);
257
311
  const assetFilePath = (0, node_path_1.resolve)(assetFolderPath, asset.filename);
@@ -284,10 +338,13 @@ class ExportAssets extends base_class_1.default {
284
338
  else {
285
339
  data.pipe(assetWriterStream);
286
340
  }
341
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `Downloaded asset: ${asset.filename || asset.uid}`, null, utils_1.PROCESS_NAMES.ASSET_DOWNLOADS);
287
342
  cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('ASSET_DOWNLOAD_SUCCESS', asset.filename, asset.uid), this.exportConfig.context);
288
343
  };
289
344
  const onReject = ({ error, additionalInfo }) => {
345
+ var _a;
290
346
  const { asset } = additionalInfo;
347
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(false, `Failed to download asset: ${asset.filename || asset.uid}`, null, utils_1.PROCESS_NAMES.ASSET_DOWNLOADS);
291
348
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.exportConfig.context), { uid: asset.uid, filename: asset.filename }), cli_utilities_1.messageHandler.parse('ASSET_DOWNLOAD_FAILED', asset.filename, asset.uid));
292
349
  };
293
350
  const promisifyHandler = (input) => {
@@ -1,3 +1,4 @@
1
+ import { CLIProgressManager } from '@contentstack/cli-utilities';
1
2
  import { ExportConfig, ModuleClassParams } from '../../types';
2
3
  export type ApiOptions = {
3
4
  uid?: string;
@@ -27,7 +28,23 @@ export type ApiModuleType = 'stack' | 'asset' | 'assets' | 'entry' | 'entries' |
27
28
  export default abstract class BaseClass {
28
29
  readonly client: any;
29
30
  exportConfig: ExportConfig;
31
+ protected progressManager: CLIProgressManager | null;
32
+ protected currentModuleName: string;
30
33
  constructor({ exportConfig, stackAPIClient }: Omit<ModuleClassParams, 'moduleName'>);
34
+ static printFinalSummary(): void;
35
+ /**
36
+ * Create simple progress manager
37
+ */
38
+ protected createSimpleProgress(moduleName: string, total?: number): CLIProgressManager;
39
+ /**
40
+ * Create nested progress manager
41
+ */
42
+ protected createNestedProgress(moduleName: string): CLIProgressManager;
43
+ /**
44
+ * Complete progress manager
45
+ */
46
+ protected completeProgress(success?: boolean, error?: string): void;
47
+ protected withLoadingSpinner<T>(message: string, action: () => Promise<T>): Promise<T>;
31
48
  get stack(): any;
32
49
  delay(ms: number): Promise<void>;
33
50
  makeConcurrentCall(env: EnvType, promisifyHandler?: CustomPromiseHandler): Promise<void>;
@@ -11,9 +11,54 @@ const isEqual_1 = tslib_1.__importDefault(require("lodash/isEqual"));
11
11
  const cli_utilities_1 = require("@contentstack/cli-utilities");
12
12
  class BaseClass {
13
13
  constructor({ exportConfig, stackAPIClient }) {
14
+ this.progressManager = null;
15
+ this.currentModuleName = '';
14
16
  this.client = stackAPIClient;
15
17
  this.exportConfig = exportConfig;
16
18
  }
19
+ static printFinalSummary() {
20
+ cli_utilities_1.CLIProgressManager.printGlobalSummary();
21
+ }
22
+ /**
23
+ * Create simple progress manager
24
+ */
25
+ createSimpleProgress(moduleName, total) {
26
+ var _a;
27
+ this.currentModuleName = moduleName;
28
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
29
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false; // Default to true for better UX
30
+ this.progressManager = cli_utilities_1.CLIProgressManager.createSimple(moduleName, total, showConsoleLogs);
31
+ return this.progressManager;
32
+ }
33
+ /**
34
+ * Create nested progress manager
35
+ */
36
+ createNestedProgress(moduleName) {
37
+ var _a;
38
+ this.currentModuleName = moduleName;
39
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
40
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false; // Default to true for better UX
41
+ this.progressManager = cli_utilities_1.CLIProgressManager.createNested(moduleName, showConsoleLogs);
42
+ return this.progressManager;
43
+ }
44
+ /**
45
+ * Complete progress manager
46
+ */
47
+ completeProgress(success = true, error) {
48
+ var _a;
49
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.complete(success, error);
50
+ this.progressManager = null;
51
+ }
52
+ async withLoadingSpinner(message, action) {
53
+ var _a;
54
+ const logConfig = cli_utilities_1.configHandler.get('log') || {};
55
+ const showConsoleLogs = (_a = logConfig.showConsoleLogs) !== null && _a !== void 0 ? _a : false;
56
+ if (showConsoleLogs) {
57
+ // If console logs are enabled, don't show spinner, just execute the action
58
+ return await action();
59
+ }
60
+ return await cli_utilities_1.CLIProgressManager.withLoadingSpinner(message, action);
61
+ }
17
62
  get stack() {
18
63
  return this.client;
19
64
  }
@@ -24,19 +24,36 @@ class ContentTypesExport extends base_class_1.default {
24
24
  this.applyQueryFilters(this.qs, 'content-types');
25
25
  this.contentTypesDirPath = path.resolve((0, cli_utilities_1.sanitizePath)(exportConfig.data), (0, cli_utilities_1.sanitizePath)(exportConfig.branchName || ''), (0, cli_utilities_1.sanitizePath)(this.contentTypesConfig.dirName));
26
26
  this.contentTypes = [];
27
- this.exportConfig.context.module = 'content-types';
27
+ this.exportConfig.context.module = utils_1.MODULE_CONTEXTS.CONTENT_TYPES;
28
+ this.currentModuleName = utils_1.MODULE_NAMES[utils_1.MODULE_CONTEXTS.CONTENT_TYPES];
28
29
  }
29
30
  async start() {
30
31
  try {
31
32
  cli_utilities_1.log.debug('Starting content types export process...', this.exportConfig.context);
32
33
  await utils_1.fsUtil.makeDirectory(this.contentTypesDirPath);
33
- cli_utilities_1.log.debug(`Created directory at path: ${this.contentTypesDirPath}`, this.exportConfig.context);
34
+ // Get content types count for progress tracking
35
+ const [totalCount] = await this.withLoadingSpinner('CONTENT-TYPES: Analyzing content types...', async () => {
36
+ const countResponse = await this.stackAPIClient
37
+ .contentType()
38
+ .query(Object.assign(Object.assign({}, this.qs), { limit: 1, include_count: true }))
39
+ .find();
40
+ return [countResponse.count || 0];
41
+ });
42
+ if (totalCount === 0) {
43
+ cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('CONTENT_TYPE_NO_TYPES'), this.exportConfig.context);
44
+ return;
45
+ }
46
+ // Create simple progress manager with total count
47
+ const progress = this.createSimpleProgress(this.currentModuleName, totalCount);
48
+ progress.updateStatus('Fetching content types...');
34
49
  await this.getContentTypes();
35
50
  await this.writeContentTypes(this.contentTypes);
36
51
  cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('CONTENT_TYPE_EXPORT_COMPLETE'), this.exportConfig.context);
52
+ this.completeProgress(true);
37
53
  }
38
54
  catch (error) {
39
55
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.exportConfig.context));
56
+ this.completeProgress(false, (error === null || error === void 0 ? void 0 : error.message) || 'Content types export failed');
40
57
  }
41
58
  }
42
59
  async getContentTypes(skip = 0) {
@@ -62,9 +79,11 @@ class ContentTypesExport extends base_class_1.default {
62
79
  }
63
80
  }
64
81
  sanitizeAttribs(contentTypes) {
65
- cli_utilities_1.log.debug(`Sanitizing ${contentTypes === null || contentTypes === void 0 ? void 0 : contentTypes.length} content types`, this.exportConfig.context);
82
+ cli_utilities_1.log.debug(`Sanitizing ${contentTypes.length} content types`, this.exportConfig.context);
66
83
  const updatedContentTypes = [];
67
84
  contentTypes.forEach((contentType) => {
85
+ var _a;
86
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `content-type: ${contentType.uid}`);
68
87
  for (const key in contentType) {
69
88
  if (this.contentTypesConfig.validKeys.indexOf(key) === -1) {
70
89
  delete contentType[key];
@@ -75,11 +94,14 @@ class ContentTypesExport extends base_class_1.default {
75
94
  return updatedContentTypes;
76
95
  }
77
96
  async writeContentTypes(contentTypes) {
78
- cli_utilities_1.log.debug(`Writing ${contentTypes === null || contentTypes === void 0 ? void 0 : contentTypes.length} content types to disk`, this.exportConfig.context);
79
- function write(contentType) {
80
- return utils_1.fsUtil.writeFile(path.join((0, cli_utilities_1.sanitizePath)(this.contentTypesDirPath), (0, cli_utilities_1.sanitizePath)(`${contentType.uid === 'schema' ? 'schema|1' : contentType.uid}.json`)), contentType);
81
- }
82
- await (0, utils_1.executeTask)(contentTypes, write.bind(this), {
97
+ cli_utilities_1.log.debug(`Writing ${contentTypes.length} content types to disk`, this.exportConfig.context);
98
+ const writeWithProgress = (contentType) => {
99
+ const filename = `${contentType.uid === 'schema' ? 'schema|1' : contentType.uid}.json`;
100
+ const fullPath = path.join((0, cli_utilities_1.sanitizePath)(this.contentTypesDirPath), (0, cli_utilities_1.sanitizePath)(filename));
101
+ cli_utilities_1.log.debug(`Writing content type to: ${fullPath}`, this.exportConfig.context);
102
+ return utils_1.fsUtil.writeFile(fullPath, contentType);
103
+ };
104
+ await (0, utils_1.executeTask)(contentTypes, writeWithProgress.bind(this), {
83
105
  concurrency: this.exportConfig.writeConcurrency,
84
106
  });
85
107
  const schemaFilePath = path.join(this.contentTypesDirPath, 'schema.json');
@@ -17,21 +17,55 @@ class ExportCustomRoles extends base_class_1.default {
17
17
  this.existingRoles = { Admin: 1, Developer: 1, 'Content Manager': 1 };
18
18
  this.localesMap = {};
19
19
  this.sourceLocalesMap = {};
20
- this.exportConfig.context.module = 'custom-roles';
20
+ this.exportConfig.context.module = utils_1.MODULE_CONTEXTS.CUSTOM_ROLES;
21
+ this.currentModuleName = utils_1.MODULE_NAMES[utils_1.MODULE_CONTEXTS.CUSTOM_ROLES];
21
22
  }
22
23
  async start() {
23
- var _a;
24
- cli_utilities_1.log.debug('Starting custom roles export process...', this.exportConfig.context);
25
- this.rolesFolderPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.customRolesConfig.dirName);
26
- cli_utilities_1.log.debug(`Custom roles folder path: ${this.rolesFolderPath}`, this.exportConfig.context);
27
- await utils_1.fsUtil.makeDirectory(this.rolesFolderPath);
28
- cli_utilities_1.log.debug('Created custom roles directory', this.exportConfig.context);
29
- this.customRolesLocalesFilepath = (0, node_path_1.resolve)(this.rolesFolderPath, this.customRolesConfig.customRolesLocalesFileName);
30
- cli_utilities_1.log.debug(`Custom roles locales file path: ${this.customRolesLocalesFilepath}`, this.exportConfig.context);
31
- await this.getCustomRoles();
32
- await this.getLocales();
33
- await this.getCustomRolesLocales();
34
- cli_utilities_1.log.debug(`Custom roles export completed. Total custom roles: ${(_a = Object.keys(this.customRoles)) === null || _a === void 0 ? void 0 : _a.length}`, this.exportConfig.context);
24
+ try {
25
+ cli_utilities_1.log.debug('Starting custom roles export process...', this.exportConfig.context);
26
+ const [totalRoles, totalLocales] = await this.withLoadingSpinner('CUSTOM-ROLES: Analyzing roles and locales...', async () => {
27
+ var _a;
28
+ this.rolesFolderPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.customRolesConfig.dirName);
29
+ await utils_1.fsUtil.makeDirectory(this.rolesFolderPath);
30
+ this.customRolesLocalesFilepath = (0, node_path_1.resolve)(this.rolesFolderPath, this.customRolesConfig.customRolesLocalesFileName);
31
+ // Get counts for progress tracking
32
+ const rolesResponse = await this.stack.role().fetchAll({ include_rules: true, include_permissions: true });
33
+ const customRolesCount = (_a = rolesResponse === null || rolesResponse === void 0 ? void 0 : rolesResponse.items) === null || _a === void 0 ? void 0 : _a.filter((role) => !this.existingRoles[role.name]).length;
34
+ const localesResponse = await this.stack.locale().query({ include_count: true, limit: 1 }).find();
35
+ const localesCount = (localesResponse === null || localesResponse === void 0 ? void 0 : localesResponse.count) || 0;
36
+ return [customRolesCount, localesCount];
37
+ });
38
+ if (totalRoles === 0) {
39
+ cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('ROLES_NO_CUSTOM_ROLES'), this.exportConfig.context);
40
+ return;
41
+ }
42
+ // Create nested progress manager
43
+ const progress = this.createNestedProgress(this.currentModuleName)
44
+ .addProcess(utils_1.PROCESS_NAMES.FETCH_ROLES, totalRoles)
45
+ .addProcess(utils_1.PROCESS_NAMES.FETCH_LOCALES, totalLocales)
46
+ .addProcess(utils_1.PROCESS_NAMES.PROCESS_MAPPINGS, 1);
47
+ progress
48
+ .startProcess(utils_1.PROCESS_NAMES.FETCH_ROLES)
49
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.FETCH_ROLES].FETCHING, utils_1.PROCESS_NAMES.FETCH_ROLES);
50
+ await this.getCustomRoles();
51
+ progress.completeProcess(utils_1.PROCESS_NAMES.FETCH_ROLES, true);
52
+ progress
53
+ .startProcess(utils_1.PROCESS_NAMES.FETCH_LOCALES)
54
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.FETCH_LOCALES].FETCHING, utils_1.PROCESS_NAMES.FETCH_LOCALES);
55
+ await this.getLocales();
56
+ progress.completeProcess(utils_1.PROCESS_NAMES.FETCH_LOCALES, true);
57
+ progress
58
+ .startProcess(utils_1.PROCESS_NAMES.PROCESS_MAPPINGS)
59
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.PROCESS_MAPPINGS].PROCESSING, utils_1.PROCESS_NAMES.PROCESS_MAPPINGS);
60
+ await this.getCustomRolesLocales();
61
+ progress.completeProcess(utils_1.PROCESS_NAMES.PROCESS_MAPPINGS, true);
62
+ cli_utilities_1.log.debug(`Custom roles export completed. Total custom roles: ${Object.keys(this.customRoles || {}).length}`, this.exportConfig.context);
63
+ this.completeProgress(true);
64
+ }
65
+ catch (error) {
66
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.exportConfig.context));
67
+ this.completeProgress(false, (error === null || error === void 0 ? void 0 : error.message) || 'Custom roles export failed');
68
+ }
35
69
  }
36
70
  async getCustomRoles() {
37
71
  var _a;
@@ -55,9 +89,11 @@ class ExportCustomRoles extends base_class_1.default {
55
89
  return;
56
90
  }
57
91
  customRoles.forEach((role) => {
58
- cli_utilities_1.log.debug(`Processing custom role: ${role === null || role === void 0 ? void 0 : role.name} (${role === null || role === void 0 ? void 0 : role.uid})`, this.exportConfig.context);
59
- cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('ROLES_EXPORTING_ROLE', role === null || role === void 0 ? void 0 : role.name), this.exportConfig.context);
92
+ var _a;
93
+ cli_utilities_1.log.debug(`Processing custom role: ${role.name} (${role.uid})`, this.exportConfig.context);
94
+ cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('ROLES_EXPORTING_ROLE', role.name), this.exportConfig.context);
60
95
  this.customRoles[role.uid] = role;
96
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `role: ${role.name}`, null, utils_1.PROCESS_NAMES.FETCH_ROLES);
61
97
  });
62
98
  const customRolesFilePath = (0, node_path_1.resolve)(this.rolesFolderPath, this.customRolesConfig.fileName);
63
99
  cli_utilities_1.log.debug(`Writing custom roles to: ${customRolesFilePath}`, this.exportConfig.context);
@@ -79,21 +115,23 @@ class ExportCustomRoles extends base_class_1.default {
79
115
  cli_utilities_1.log.debug('Error occurred while fetching locales', this.exportConfig.context);
80
116
  return (0, cli_utilities_1.handleAndLogError)(err, Object.assign({}, this.exportConfig.context));
81
117
  });
82
- for (const locale of locales.items) {
83
- cli_utilities_1.log.debug(`Mapping locale: ${locale === null || locale === void 0 ? void 0 : locale.name} (${locale === null || locale === void 0 ? void 0 : locale.uid})`, this.exportConfig.context);
118
+ for (const locale of locales === null || locales === void 0 ? void 0 : locales.items) {
119
+ cli_utilities_1.log.debug(`Mapping locale: ${locale.name} (${locale.uid})`, this.exportConfig.context);
84
120
  this.sourceLocalesMap[locale.uid] = locale;
121
+ // Track progress for each locale
122
+ (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `locale: ${locale.name}`, null, utils_1.PROCESS_NAMES.FETCH_LOCALES);
85
123
  }
86
- cli_utilities_1.log.debug(`Mapped ${(_a = Object.keys(this.sourceLocalesMap)) === null || _a === void 0 ? void 0 : _a.length} locales`, this.exportConfig.context);
124
+ cli_utilities_1.log.debug(`Mapped ${Object.keys(this.sourceLocalesMap || {}).length} locales`, this.exportConfig.context);
87
125
  }
88
126
  async getCustomRolesLocales() {
89
127
  var _a, _b, _c;
90
128
  cli_utilities_1.log.debug('Processing custom roles locales mapping...', this.exportConfig.context);
91
129
  for (const role of (0, values_1.default)(this.customRoles)) {
92
130
  const customRole = role;
93
- cli_utilities_1.log.debug(`Processing locales for custom role: ${customRole === null || customRole === void 0 ? void 0 : customRole.name}`, this.exportConfig.context);
131
+ cli_utilities_1.log.debug(`Processing locales for custom role: ${customRole.name}`, this.exportConfig.context);
94
132
  const rulesLocales = (0, find_1.default)(customRole.rules, (rule) => rule.module === 'locale');
95
133
  if ((_a = rulesLocales === null || rulesLocales === void 0 ? void 0 : rulesLocales.locales) === null || _a === void 0 ? void 0 : _a.length) {
96
- cli_utilities_1.log.debug(`Found ${rulesLocales.locales.length} locales for role: ${customRole === null || customRole === void 0 ? void 0 : customRole.name}`, this.exportConfig.context);
134
+ cli_utilities_1.log.debug(`Found ${rulesLocales.locales.length} locales for role: ${customRole.name}`, this.exportConfig.context);
97
135
  (0, forEach_1.default)(rulesLocales.locales, (locale) => {
98
136
  cli_utilities_1.log.debug(`Adding locale ${locale} to custom roles mapping`, this.exportConfig.context);
99
137
  this.localesMap[locale] = 1;
@@ -101,7 +139,7 @@ class ExportCustomRoles extends base_class_1.default {
101
139
  }
102
140
  }
103
141
  if ((_b = (0, keys_1.default)(this.localesMap)) === null || _b === void 0 ? void 0 : _b.length) {
104
- cli_utilities_1.log.debug(`Processing ${(_c = (0, keys_1.default)(this.localesMap)) === null || _c === void 0 ? void 0 : _c.length} custom role locales`, this.exportConfig.context);
142
+ cli_utilities_1.log.debug(`Processing ${(0, keys_1.default)(this.localesMap).length} custom role locales`, this.exportConfig.context);
105
143
  for (const locale in this.localesMap) {
106
144
  if (this.sourceLocalesMap[locale] !== undefined) {
107
145
  const sourceLocale = this.sourceLocalesMap[locale];
@@ -116,6 +154,8 @@ class ExportCustomRoles extends base_class_1.default {
116
154
  else {
117
155
  cli_utilities_1.log.debug('No custom role locales found to process', this.exportConfig.context);
118
156
  }
157
+ // Track progress for mapping completion
158
+ (_c = this.progressManager) === null || _c === void 0 ? void 0 : _c.tick(true, 'role-locale mappings', null, utils_1.PROCESS_NAMES.PROCESS_MAPPINGS);
119
159
  }
120
160
  }
121
161
  exports.default = ExportCustomRoles;
@@ -13,6 +13,8 @@ export default class EntriesExport extends BaseClass {
13
13
  exportVariantEntry: boolean;
14
14
  constructor({ exportConfig, stackAPIClient }: ModuleClassParams);
15
15
  start(): Promise<void>;
16
+ getTotalEntriesCount(entryRequestOptions: Array<Record<string, any>>): Promise<number>;
17
+ setupVariantExport(): Promise<any>;
16
18
  createRequestObjects(locales: Array<Record<string, unknown>>, contentTypes: Array<Record<string, unknown>>): Array<Record<string, any>>;
17
19
  getEntries(options: Record<string, any>): Promise<any>;
18
20
  fetchEntriesVersions(entries: any, options: {