@contentstack/cli-cm-export 2.0.0-beta.1 → 2.0.0-beta.10

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 (43) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +11 -65
  3. package/lib/commands/cm/stacks/export.d.ts +0 -1
  4. package/lib/commands/cm/stacks/export.js +12 -37
  5. package/lib/config/index.js +7 -0
  6. package/lib/constants/index.d.ts +57 -0
  7. package/lib/constants/index.js +59 -0
  8. package/lib/export/module-exporter.js +3 -2
  9. package/lib/export/modules/assets.js +5 -5
  10. package/lib/export/modules/base-class.d.ts +17 -0
  11. package/lib/export/modules/base-class.js +29 -1
  12. package/lib/export/modules/composable-studio.d.ts +15 -0
  13. package/lib/export/modules/composable-studio.js +87 -0
  14. package/lib/export/modules/content-types.js +6 -8
  15. package/lib/export/modules/custom-roles.js +2 -2
  16. package/lib/export/modules/entries.d.ts +1 -1
  17. package/lib/export/modules/entries.js +18 -19
  18. package/lib/export/modules/environments.js +2 -3
  19. package/lib/export/modules/extensions.js +2 -3
  20. package/lib/export/modules/global-fields.js +8 -5
  21. package/lib/export/modules/labels.js +2 -3
  22. package/lib/export/modules/locales.js +2 -3
  23. package/lib/export/modules/marketplace-apps.js +36 -25
  24. package/lib/export/modules/personalize.js +1 -2
  25. package/lib/export/modules/stack.js +29 -29
  26. package/lib/export/modules/taxonomies.d.ts +52 -8
  27. package/lib/export/modules/taxonomies.js +278 -104
  28. package/lib/export/modules/webhooks.js +2 -3
  29. package/lib/export/modules/workflows.js +3 -4
  30. package/lib/types/default-config.d.ts +6 -0
  31. package/lib/types/index.d.ts +27 -2
  32. package/lib/utils/basic-login.d.ts +1 -1
  33. package/lib/utils/basic-login.js +5 -5
  34. package/lib/utils/common-helper.d.ts +1 -1
  35. package/lib/utils/common-helper.js +4 -4
  36. package/lib/utils/export-config-handler.js +10 -13
  37. package/lib/utils/file-helper.js +1 -1
  38. package/lib/utils/logger.d.ts +1 -1
  39. package/lib/utils/logger.js +2 -2
  40. package/lib/utils/marketplace-app-helper.js +2 -4
  41. package/messages/index.json +73 -67
  42. package/oclif.manifest.json +4 -42
  43. package/package.json +18 -15
@@ -11,63 +11,45 @@ const utils_1 = require("../../utils");
11
11
  class ExportTaxonomies extends base_class_1.default {
12
12
  constructor({ exportConfig, stackAPIClient }) {
13
13
  super({ exportConfig, stackAPIClient });
14
+ this.isLocaleBasedExportSupported = true; // Flag to track if locale-based export is supported
14
15
  this.taxonomies = {};
16
+ this.taxonomiesByLocale = {};
15
17
  this.taxonomiesConfig = exportConfig.modules.taxonomies;
16
18
  this.qs = { include_count: true, limit: this.taxonomiesConfig.limit || 100, skip: 0 };
17
19
  this.applyQueryFilters(this.qs, 'taxonomies');
18
20
  this.exportConfig.context.module = utils_1.MODULE_CONTEXTS.TAXONOMIES;
19
21
  this.currentModuleName = utils_1.MODULE_NAMES[utils_1.MODULE_CONTEXTS.TAXONOMIES];
22
+ this.localesFilePath = (0, node_path_1.resolve)((0, cli_utilities_1.sanitizePath)(exportConfig.exportDir), (0, cli_utilities_1.sanitizePath)(exportConfig.branchName || ''), (0, cli_utilities_1.sanitizePath)(exportConfig.modules.locales.dirName), (0, cli_utilities_1.sanitizePath)(exportConfig.modules.locales.fileName));
20
23
  }
21
24
  async start() {
22
25
  var _a;
23
26
  try {
24
- cli_utilities_1.log.debug('Starting taxonomies export process...', this.exportConfig.context);
25
- // Setup with loading spinner
26
- const [totalCount] = await this.withLoadingSpinner('TAXONOMIES: Analyzing taxonomy structure...', async () => {
27
- this.taxonomiesFolderPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.taxonomiesConfig.dirName);
28
- await utils_1.fsUtil.makeDirectory(this.taxonomiesFolderPath);
29
- // Get count first for progress tracking
30
- const countResponse = await this.stack
31
- .taxonomy()
32
- .query(Object.assign(Object.assign({}, this.qs), { include_count: true, limit: 1 }))
33
- .find();
34
- return [countResponse.count || 0];
35
- });
27
+ cli_utilities_1.log.debug('Starting export process for taxonomies...', this.exportConfig.context);
28
+ const totalCount = await this.initializeExport();
36
29
  if (totalCount === 0) {
37
30
  cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('TAXONOMY_NOT_FOUND'), this.exportConfig.context);
38
31
  return;
39
32
  }
40
- // Create nested progress manager
41
- const progress = this.createNestedProgress(this.currentModuleName);
42
- // Add sub-processes
43
- progress.addProcess(utils_1.PROCESS_NAMES.FETCH_TAXONOMIES, totalCount);
44
- progress.addProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS, totalCount);
45
- // Fetch taxonomies
33
+ const progress = this.setupProgress(totalCount);
34
+ const localesToExport = this.getLocalesToExport();
35
+ if (localesToExport.length === 0) {
36
+ cli_utilities_1.log.warn('No locales found to export', this.exportConfig.context);
37
+ this.completeProgress(true);
38
+ return;
39
+ }
40
+ // Start fetch process
46
41
  progress
47
42
  .startProcess(utils_1.PROCESS_NAMES.FETCH_TAXONOMIES)
48
43
  .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.FETCH_TAXONOMIES].FETCHING, utils_1.PROCESS_NAMES.FETCH_TAXONOMIES);
49
- await this.getAllTaxonomies();
44
+ // Determine export strategy and fetch taxonomies
45
+ await this.determineExportStrategy((_a = this.exportConfig.master_locale) === null || _a === void 0 ? void 0 : _a.code);
46
+ await this.fetchAllTaxonomies(localesToExport);
50
47
  progress.completeProcess(utils_1.PROCESS_NAMES.FETCH_TAXONOMIES, true);
51
- const actualTaxonomyCount = (_a = Object.keys(this.taxonomies || {})) === null || _a === void 0 ? void 0 : _a.length;
52
- cli_utilities_1.log.debug(`Found ${actualTaxonomyCount} taxonomies to export (API reported ${totalCount})`, this.exportConfig.context);
53
- // Update progress for export step if counts differ
54
- if (actualTaxonomyCount !== totalCount && actualTaxonomyCount > 0) {
55
- // Remove the old process and add with correct count
56
- progress.addProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS, actualTaxonomyCount);
57
- }
58
- // Export detailed taxonomies
59
- if (actualTaxonomyCount > 0) {
60
- progress
61
- .startProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS)
62
- .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS].EXPORTING, utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS);
63
- await this.exportTaxonomies();
64
- progress.completeProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS, true);
65
- }
66
- else {
67
- cli_utilities_1.log.info('No taxonomies found to export detailed information', this.exportConfig.context);
68
- }
69
- const taxonomyCount = Object.keys(this.taxonomies || {}).length;
70
- cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('TAXONOMY_EXPORT_COMPLETE', taxonomyCount), this.exportConfig.context);
48
+ // Export taxonomies with detailed information
49
+ const actualCount = await this.exportAllTaxonomies(progress, localesToExport, totalCount);
50
+ // Write metadata and complete
51
+ await this.writeTaxonomiesMetadata();
52
+ cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('TAXONOMY_EXPORT_COMPLETE', actualCount), this.exportConfig.context);
71
53
  this.completeProgress(true);
72
54
  }
73
55
  catch (error) {
@@ -76,72 +58,226 @@ class ExportTaxonomies extends base_class_1.default {
76
58
  }
77
59
  }
78
60
  /**
79
- * Fetch in the provided stack
80
- * @param {number} skip
81
- * @returns {Promise<any>}
61
+ * Initialize export setup (create directories, get initial count)
82
62
  */
83
- async getAllTaxonomies(skip = 0) {
84
- var _a, _b;
85
- if (skip) {
86
- this.qs.skip = skip;
87
- cli_utilities_1.log.debug(`Fetching taxonomies with skip: ${skip}`, this.exportConfig.context);
63
+ async initializeExport() {
64
+ return this.withLoadingSpinner('TAXONOMIES: Analyzing taxonomy structure...', async () => {
65
+ this.taxonomiesFolderPath = (0, node_path_1.resolve)(this.exportConfig.exportDir, this.exportConfig.branchName || '', this.taxonomiesConfig.dirName);
66
+ cli_utilities_1.log.debug(`Taxonomies folder path: '${this.taxonomiesFolderPath}'`, this.exportConfig.context);
67
+ await utils_1.fsUtil.makeDirectory(this.taxonomiesFolderPath);
68
+ cli_utilities_1.log.debug('Created taxonomies directory.', this.exportConfig.context);
69
+ // Get count first for progress tracking
70
+ const countResponse = await this.stack
71
+ .taxonomy()
72
+ .query(Object.assign(Object.assign({}, this.qs), { include_count: true, limit: 1 }))
73
+ .find();
74
+ return countResponse.count || 0;
75
+ });
76
+ }
77
+ /**
78
+ * Setup progress manager with processes
79
+ */
80
+ setupProgress(totalCount) {
81
+ const progress = this.createNestedProgress(this.currentModuleName);
82
+ // For fetch: count API calls, not individual taxonomies
83
+ const fetchApiCallsCount = Math.ceil(totalCount / (this.qs.limit || 100));
84
+ progress.addProcess(utils_1.PROCESS_NAMES.FETCH_TAXONOMIES, fetchApiCallsCount);
85
+ progress.addProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS, totalCount);
86
+ return progress;
87
+ }
88
+ /**
89
+ * Determine if locale-based export is supported
90
+ */
91
+ async determineExportStrategy(masterLocale) {
92
+ await this.fetchTaxonomies(masterLocale, true);
93
+ if (!this.isLocaleBasedExportSupported) {
94
+ cli_utilities_1.log.debug('Falling back to legacy export (non-localized)', this.exportConfig.context);
95
+ this.taxonomies = {};
96
+ this.taxonomiesByLocale = {};
88
97
  }
89
98
  else {
90
- cli_utilities_1.log.debug('Fetching taxonomies with initial query', this.exportConfig.context);
91
- }
92
- cli_utilities_1.log.debug(`Query parameters: ${JSON.stringify(this.qs)}`, this.exportConfig.context);
93
- let taxonomyResult = await this.stack.taxonomy().query(this.qs).find();
94
- cli_utilities_1.log.debug(`Fetched ${((_a = taxonomyResult.items) === null || _a === void 0 ? void 0 : _a.length) || 0} taxonomies out of total ${taxonomyResult.count}`, this.exportConfig.context);
95
- if ((taxonomyResult === null || taxonomyResult === void 0 ? void 0 : taxonomyResult.items) && ((_b = taxonomyResult === null || taxonomyResult === void 0 ? void 0 : taxonomyResult.items) === null || _b === void 0 ? void 0 : _b.length) > 0) {
96
- cli_utilities_1.log.debug(`Processing ${taxonomyResult.items.length} taxonomies`, this.exportConfig.context);
97
- this.sanitizeTaxonomiesAttribs(taxonomyResult.items);
98
- skip += this.taxonomiesConfig.limit;
99
- if (skip >= taxonomyResult.count) {
100
- cli_utilities_1.log.debug('Completed fetching all taxonomies', this.exportConfig.context);
101
- return;
99
+ cli_utilities_1.log.debug('Localization enabled, proceeding with locale-based export', this.exportConfig.context);
100
+ }
101
+ }
102
+ /**
103
+ * Fetch all taxonomies based on export strategy
104
+ */
105
+ async fetchAllTaxonomies(localesToExport) {
106
+ if (!this.isLocaleBasedExportSupported) {
107
+ await this.fetchTaxonomies();
108
+ }
109
+ else {
110
+ for (const localeCode of localesToExport) {
111
+ await this.fetchTaxonomies(localeCode);
102
112
  }
103
- cli_utilities_1.log.debug(`Continuing to fetch taxonomies with skip: ${skip}`, this.exportConfig.context);
104
- return await this.getAllTaxonomies(skip);
113
+ }
114
+ }
115
+ /**
116
+ * Export all taxonomies with detailed information
117
+ */
118
+ async exportAllTaxonomies(progress, localesToExport, totalCount) {
119
+ var _a;
120
+ const actualCount = (_a = Object.keys(this.taxonomies || {})) === null || _a === void 0 ? void 0 : _a.length;
121
+ cli_utilities_1.log.debug(`Found ${actualCount} taxonomies to export (API reported ${totalCount})`, this.exportConfig.context);
122
+ if (actualCount === 0) {
123
+ cli_utilities_1.log.info('No taxonomies found to export detailed information', this.exportConfig.context);
124
+ return 0;
125
+ }
126
+ // Update progress total if needed
127
+ if (actualCount !== totalCount) {
128
+ progress.updateProcessTotal(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS, actualCount);
129
+ }
130
+ // Start export process
131
+ progress
132
+ .startProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS)
133
+ .updateStatus(utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS].EXPORTING, utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS);
134
+ // Export based on strategy
135
+ if (!this.isLocaleBasedExportSupported) {
136
+ await this.exportTaxonomies();
105
137
  }
106
138
  else {
139
+ for (const localeCode of localesToExport) {
140
+ await this.processLocaleExport(localeCode);
141
+ }
142
+ }
143
+ progress.completeProcess(utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS, true);
144
+ return actualCount;
145
+ }
146
+ /**
147
+ * Process and export taxonomies for a specific locale
148
+ */
149
+ async processLocaleExport(localeCode) {
150
+ const localeTaxonomies = this.taxonomiesByLocale[localeCode];
151
+ if ((localeTaxonomies === null || localeTaxonomies === void 0 ? void 0 : localeTaxonomies.size) > 0) {
152
+ cli_utilities_1.log.info(`Found ${localeTaxonomies.size} taxonomies for locale: ${localeCode}`, this.exportConfig.context);
153
+ await this.exportTaxonomies(localeCode);
154
+ }
155
+ else {
156
+ cli_utilities_1.log.debug(`No taxonomies found for locale: ${localeCode}`, this.exportConfig.context);
157
+ }
158
+ }
159
+ /**
160
+ * Write taxonomies metadata file
161
+ */
162
+ async writeTaxonomiesMetadata() {
163
+ if (!this.taxonomies || (0, isEmpty_1.default)(this.taxonomies)) {
107
164
  cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('TAXONOMY_NOT_FOUND'), this.exportConfig.context);
165
+ return;
108
166
  }
167
+ const taxonomiesFilePath = (0, node_path_1.resolve)(this.taxonomiesFolderPath, this.taxonomiesConfig.fileName);
168
+ cli_utilities_1.log.debug(`Writing taxonomies metadata to: ${taxonomiesFilePath}`, this.exportConfig.context);
169
+ utils_1.fsUtil.writeFile(taxonomiesFilePath, this.taxonomies);
109
170
  }
110
- sanitizeTaxonomiesAttribs(taxonomies) {
111
- var _a;
112
- cli_utilities_1.log.debug(`Sanitizing ${taxonomies.length} taxonomies`, this.exportConfig.context);
113
- for (let index = 0; index < (taxonomies === null || taxonomies === void 0 ? void 0 : taxonomies.length); index++) {
114
- const taxonomy = taxonomies[index];
115
- const taxonomyUid = taxonomy.uid;
116
- const taxonomyName = taxonomy === null || taxonomy === void 0 ? void 0 : taxonomy.name;
117
- cli_utilities_1.log.debug(`Processing taxonomy: ${taxonomyName} (${taxonomyUid})`, this.exportConfig.context);
118
- if (this.taxonomiesConfig.invalidKeys && this.taxonomiesConfig.invalidKeys.length > 0) {
119
- this.taxonomies[taxonomyUid] = (0, omit_1.default)(taxonomy, this.taxonomiesConfig.invalidKeys);
171
+ /**
172
+ * Fetch taxonomies
173
+ *
174
+ * @async
175
+ * @param {?string} [localeCode]
176
+ * @param {boolean} [checkLocaleSupport=false]
177
+ * @returns {Promise<void>}
178
+ */
179
+ async fetchTaxonomies(localeCode, checkLocaleSupport = false) {
180
+ var _a, _b;
181
+ let skip = 0;
182
+ const localeInfo = localeCode ? `for locale: ${localeCode}` : '';
183
+ if (localeCode && !this.taxonomiesByLocale[localeCode]) {
184
+ this.taxonomiesByLocale[localeCode] = new Set();
185
+ }
186
+ do {
187
+ const queryParams = Object.assign(Object.assign({}, this.qs), { skip });
188
+ if (localeCode) {
189
+ queryParams.locale = localeCode;
120
190
  }
121
- else {
122
- this.taxonomies[taxonomyUid] = taxonomy;
191
+ cli_utilities_1.log.debug(`Fetching taxonomies ${localeInfo} with skip: ${skip}`, this.exportConfig.context);
192
+ try {
193
+ const data = await this.stack.taxonomy().query(queryParams).find();
194
+ const { items, count } = data;
195
+ const taxonomiesCount = (_a = count !== null && count !== void 0 ? count : items === null || items === void 0 ? void 0 : items.length) !== null && _a !== void 0 ? _a : 0;
196
+ cli_utilities_1.log.debug(`Fetched ${(items === null || items === void 0 ? void 0 : items.length) || 0} taxonomies out of total ${taxonomiesCount} ${localeInfo}`, this.exportConfig.context);
197
+ if (!(items === null || items === void 0 ? void 0 : items.length)) {
198
+ cli_utilities_1.log.debug(`No taxonomies found ${localeInfo}`, this.exportConfig.context);
199
+ break;
200
+ }
201
+ // Check localization support
202
+ if (checkLocaleSupport && localeCode && skip === 0 && !items[0].locale) {
203
+ cli_utilities_1.log.debug('API does not support locale-based taxonomy export', this.exportConfig.context);
204
+ this.isLocaleBasedExportSupported = false;
205
+ }
206
+ this.sanitizeTaxonomiesAttribs(items, localeCode);
207
+ // Track progress per API call (only for actual fetch, not locale support check)
208
+ if (!checkLocaleSupport) {
209
+ (_b = this.progressManager) === null || _b === void 0 ? void 0 : _b.tick(true, `fetched ${items.length} taxonomies${localeInfo}`, null, utils_1.PROCESS_NAMES.FETCH_TAXONOMIES);
210
+ }
211
+ skip += this.qs.limit || 100;
212
+ if (skip >= taxonomiesCount) {
213
+ cli_utilities_1.log.debug(`Completed fetching all taxonomies ${localeInfo}`, this.exportConfig.context);
214
+ break;
215
+ }
216
+ }
217
+ catch (error) {
218
+ cli_utilities_1.log.debug(`Error fetching taxonomies ${localeInfo}`, this.exportConfig.context);
219
+ if (checkLocaleSupport && this.isLocalePlanLimitationError(error)) {
220
+ cli_utilities_1.log.debug('Taxonomy localization is not included in your plan. Falling back to non-localized export.', this.exportConfig.context);
221
+ this.isLocaleBasedExportSupported = false;
222
+ }
223
+ else if (checkLocaleSupport) {
224
+ cli_utilities_1.log.debug('Locale-based taxonomy export not supported, will use legacy method', this.exportConfig.context);
225
+ this.isLocaleBasedExportSupported = false;
226
+ }
227
+ else {
228
+ // Log actual errors during normal fetch (not locale check)
229
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.exportConfig.context), (localeCode && { locale: localeCode })));
230
+ }
231
+ // Break to avoid infinite retry loop on errors
232
+ break;
233
+ }
234
+ } while (true);
235
+ }
236
+ /**
237
+ * remove invalid keys and write data into taxonomies
238
+ * @function sanitizeTaxonomiesAttribs
239
+ * @param {Record<string, string>[]} taxonomies
240
+ * @param {?string} [localeCode]
241
+ */
242
+ sanitizeTaxonomiesAttribs(taxonomies, localeCode) {
243
+ const localeInfo = localeCode ? ` for locale: ${localeCode}` : '';
244
+ cli_utilities_1.log.debug(`Processing ${taxonomies.length} taxonomies${localeInfo}`, this.exportConfig.context);
245
+ for (const taxonomy of taxonomies) {
246
+ const taxonomyUID = taxonomy.uid;
247
+ const taxonomyName = taxonomy.name;
248
+ cli_utilities_1.log.debug(`Processing taxonomy: ${taxonomyName} (${taxonomyUID})${localeInfo}`, this.exportConfig.context);
249
+ // Store taxonomy metadata (only once per taxonomy)
250
+ if (!this.taxonomies[taxonomyUID]) {
251
+ this.taxonomies[taxonomyUID] = (0, omit_1.default)(taxonomy, this.taxonomiesConfig.invalidKeys);
252
+ }
253
+ // Track taxonomy for this locale
254
+ if (localeCode) {
255
+ this.taxonomiesByLocale[localeCode].add(taxonomyUID);
123
256
  }
124
- // Track progress for each taxonomy
125
- (_a = this.progressManager) === null || _a === void 0 ? void 0 : _a.tick(true, `taxonomy: ${taxonomyName || taxonomyUid}`, null, utils_1.PROCESS_NAMES.FETCH_TAXONOMIES);
126
257
  }
127
- cli_utilities_1.log.debug(`Sanitization complete. Total taxonomies processed: ${Object.keys(this.taxonomies || {}).length}`, this.exportConfig.context);
258
+ cli_utilities_1.log.debug(`Processing complete${localeInfo}. Total taxonomies processed: ${(0, keys_1.default)(this.taxonomies).length}`, this.exportConfig.context);
128
259
  }
129
260
  /**
130
- * Export all taxonomies details using metadata(this.taxonomies) and write it into respective <taxonomy-uid>.json file
131
- * @returns {Promise<any>}
261
+ * Export taxonomies - supports both locale-based and legacy export
132
262
  */
133
- async exportTaxonomies() {
134
- var _a;
135
- cli_utilities_1.log.debug(`Exporting ${(_a = Object.keys(this.taxonomies || {})) === null || _a === void 0 ? void 0 : _a.length} taxonomies with detailed information`, this.exportConfig.context);
136
- if ((0, isEmpty_1.default)(this.taxonomies)) {
137
- cli_utilities_1.log.info(cli_utilities_1.messageHandler.parse('TAXONOMY_NOT_FOUND'), this.exportConfig.context);
263
+ async exportTaxonomies(localeCode) {
264
+ const taxonomiesUID = localeCode ? Array.from(this.taxonomiesByLocale[localeCode] || []) : (0, keys_1.default)(this.taxonomies);
265
+ const localeInfo = localeCode ? ` for locale: ${localeCode}` : '';
266
+ if (taxonomiesUID.length === 0) {
267
+ cli_utilities_1.log.debug(`No taxonomies to export${localeInfo}`, this.exportConfig.context);
138
268
  return;
139
269
  }
270
+ cli_utilities_1.log.debug(`Exporting detailed data for ${taxonomiesUID.length} taxonomies${localeInfo}`, this.exportConfig.context);
271
+ const exportFolderPath = localeCode ? (0, node_path_1.resolve)(this.taxonomiesFolderPath, localeCode) : this.taxonomiesFolderPath;
272
+ if (localeCode) {
273
+ await utils_1.fsUtil.makeDirectory(exportFolderPath);
274
+ cli_utilities_1.log.debug(`Created locale folder: ${exportFolderPath}`, this.exportConfig.context);
275
+ }
140
276
  const onSuccess = ({ response, uid }) => {
141
277
  var _a, _b;
142
278
  const taxonomyName = (_a = this.taxonomies[uid]) === null || _a === void 0 ? void 0 : _a.name;
143
- const filePath = (0, node_path_1.resolve)(this.taxonomiesFolderPath, `${uid}.json`);
144
- cli_utilities_1.log.debug(`Writing detailed taxonomy to: ${filePath}`, this.exportConfig.context);
279
+ const filePath = (0, node_path_1.resolve)(exportFolderPath, `${uid}.json`);
280
+ cli_utilities_1.log.debug(`Writing detailed taxonomy data to: ${filePath}`, this.exportConfig.context);
145
281
  utils_1.fsUtil.writeFile(filePath, response);
146
282
  // Track progress for each exported taxonomy
147
283
  (_b = this.progressManager) === null || _b === void 0 ? void 0 : _b.tick(true, `taxonomy: ${taxonomyName || uid}`, null, utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS);
@@ -150,31 +286,69 @@ class ExportTaxonomies extends base_class_1.default {
150
286
  const onReject = ({ error, uid }) => {
151
287
  var _a, _b;
152
288
  const taxonomyName = (_a = this.taxonomies[uid]) === null || _a === void 0 ? void 0 : _a.name;
289
+ cli_utilities_1.log.debug(`Failed to export detailed data for taxonomy: ${uid}${localeInfo}`, this.exportConfig.context);
153
290
  // Track failure
154
291
  (_b = this.progressManager) === null || _b === void 0 ? void 0 : _b.tick(false, `taxonomy: ${taxonomyName || uid}`, (error === null || error === void 0 ? void 0 : error.message) || utils_1.PROCESS_STATUS[utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS].FAILED, utils_1.PROCESS_NAMES.EXPORT_TAXONOMIES_TERMS);
155
- (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.exportConfig.context), { uid }), cli_utilities_1.messageHandler.parse('TAXONOMY_EXPORT_FAILED', taxonomyName || uid));
292
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign(Object.assign({}, this.exportConfig.context), { uid }), (localeCode && { locale: localeCode })));
156
293
  };
157
- const taxonomyUids = (0, keys_1.default)(this.taxonomies);
158
- cli_utilities_1.log.debug(`Starting detailed export for ${taxonomyUids.length} taxonomies`, this.exportConfig.context);
159
- // Export each taxonomy individually
160
- for (const uid of taxonomyUids) {
161
- try {
162
- cli_utilities_1.log.debug(`Exporting detailed taxonomy: ${uid}`, this.exportConfig.context);
163
- await this.makeAPICall({
164
- module: 'export-taxonomy',
165
- uid,
166
- resolve: onSuccess,
167
- reject: onReject,
168
- });
294
+ for (const taxonomyUID of taxonomiesUID) {
295
+ cli_utilities_1.log.debug(`Processing detailed export for taxonomy: ${taxonomyUID}${localeInfo}`, this.exportConfig.context);
296
+ const exportParams = { format: 'json' };
297
+ if (localeCode) {
298
+ exportParams.locale = localeCode;
299
+ if (this.qs.include_fallback !== undefined)
300
+ exportParams.include_fallback = this.qs.include_fallback;
301
+ if (this.qs.fallback_locale)
302
+ exportParams.fallback_locale = this.qs.fallback_locale;
169
303
  }
170
- catch (error) {
171
- onReject({ error, uid });
304
+ if (this.qs.branch)
305
+ exportParams.branch = this.qs.branch;
306
+ await this.makeAPICall({
307
+ reject: onReject,
308
+ resolve: onSuccess,
309
+ uid: taxonomyUID,
310
+ module: 'export-taxonomy',
311
+ queryParam: exportParams,
312
+ });
313
+ }
314
+ cli_utilities_1.log.debug(`Completed detailed taxonomy export process${localeInfo}`, this.exportConfig.context);
315
+ }
316
+ /**
317
+ * Get all locales to export
318
+ */
319
+ getLocalesToExport() {
320
+ var _a;
321
+ cli_utilities_1.log.debug('Determining locales to export...', this.exportConfig.context);
322
+ const masterLocaleCode = ((_a = this.exportConfig.master_locale) === null || _a === void 0 ? void 0 : _a.code) || 'en-us';
323
+ const localeSet = new Set([masterLocaleCode]);
324
+ try {
325
+ const locales = utils_1.fsUtil.readFile(this.localesFilePath);
326
+ if (locales && (0, keys_1.default)(locales || {}).length > 0) {
327
+ cli_utilities_1.log.debug(`Loaded ${(0, keys_1.default)(locales || {}).length} locales from ${this.localesFilePath}`, this.exportConfig.context);
328
+ for (const localeUid of (0, keys_1.default)(locales)) {
329
+ const localeCode = locales[localeUid].code;
330
+ if (localeCode && !localeSet.has(localeCode)) {
331
+ localeSet.add(localeCode);
332
+ cli_utilities_1.log.debug(`Added locale: ${localeCode} (uid: ${localeUid})`, this.exportConfig.context);
333
+ }
334
+ }
335
+ }
336
+ else {
337
+ cli_utilities_1.log.debug(`No locales found in ${this.localesFilePath}`, this.exportConfig.context);
172
338
  }
173
339
  }
174
- // Write the taxonomies index file
175
- const taxonomiesFilePath = (0, node_path_1.resolve)(this.taxonomiesFolderPath, this.taxonomiesConfig.fileName);
176
- cli_utilities_1.log.debug(`Writing taxonomies index to: ${taxonomiesFilePath}`, this.exportConfig.context);
177
- utils_1.fsUtil.writeFile(taxonomiesFilePath, this.taxonomies);
340
+ catch (error) {
341
+ cli_utilities_1.log.warn(`Failed to read locales file: ${this.localesFilePath}`, this.exportConfig.context);
342
+ }
343
+ const localesToExport = Array.from(localeSet);
344
+ cli_utilities_1.log.debug(`Total unique locales to export: ${localesToExport.length}`, this.exportConfig.context);
345
+ return localesToExport;
346
+ }
347
+ isLocalePlanLimitationError(error) {
348
+ var _a, _b;
349
+ return ((error === null || error === void 0 ? void 0 : error.status) === 403 &&
350
+ ((_b = (_a = error === null || error === void 0 ? void 0 : error.errors) === null || _a === void 0 ? void 0 : _a.taxonomies) === null || _b === void 0 ? void 0 : _b.some((msg) => msg.toLowerCase().includes('taxonomy localization') &&
351
+ msg.toLowerCase().includes('not included in your plan'))));
178
352
  }
179
353
  }
180
354
  exports.default = ExportTaxonomies;
@@ -21,7 +21,7 @@ class ExportWebhooks extends base_class_1.default {
21
21
  cli_utilities_1.log.debug('Starting webhooks export process...', this.exportConfig.context);
22
22
  // Setup with loading spinner
23
23
  const [totalCount] = await this.withLoadingSpinner('WEBHOOKS: Analyzing webhooks...', async () => {
24
- this.webhooksFolderPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.webhookConfig.dirName);
24
+ this.webhooksFolderPath = (0, node_path_1.resolve)(this.exportConfig.exportDir, this.exportConfig.branchName || '', this.webhookConfig.dirName);
25
25
  await utils_1.fsUtil.makeDirectory(this.webhooksFolderPath);
26
26
  // Get count for progress tracking
27
27
  const countResponse = await this.stack.webhook().fetchAll(Object.assign(Object.assign({}, this.qs), { limit: 1 }));
@@ -43,9 +43,8 @@ class ExportWebhooks extends base_class_1.default {
43
43
  const webhooksFilePath = (0, node_path_1.resolve)(this.webhooksFolderPath, this.webhookConfig.fileName);
44
44
  cli_utilities_1.log.debug(`Writing webhooks to: ${webhooksFilePath}`, this.exportConfig.context);
45
45
  utils_1.fsUtil.writeFile(webhooksFilePath, this.webhooks);
46
- cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('WEBHOOK_EXPORT_COMPLETE', Object.keys(this.webhooks || {}).length), this.exportConfig.context);
47
46
  }
48
- this.completeProgress(true);
47
+ this.completeProgressWithMessage();
49
48
  }
50
49
  catch (error) {
51
50
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.exportConfig.context));
@@ -22,7 +22,7 @@ class ExportWorkFlows extends base_class_1.default {
22
22
  // Setup with loading spinner
23
23
  const [totalCount] = await this.withLoadingSpinner('WORKFLOWS: Analyzing workflows...', async () => {
24
24
  var _a;
25
- this.webhooksFolderPath = (0, node_path_1.resolve)(this.exportConfig.data, this.exportConfig.branchName || '', this.workflowConfig.dirName);
25
+ this.webhooksFolderPath = (0, node_path_1.resolve)(this.exportConfig.exportDir, this.exportConfig.branchName || '', this.workflowConfig.dirName);
26
26
  await utils_1.fsUtil.makeDirectory(this.webhooksFolderPath);
27
27
  // Get count for progress tracking
28
28
  const countResponse = await this.stack.workflow().fetchAll(Object.assign(Object.assign({}, this.qs), { limit: 1 }));
@@ -46,9 +46,8 @@ class ExportWorkFlows extends base_class_1.default {
46
46
  const workflowsFilePath = (0, node_path_1.resolve)(this.webhooksFolderPath, this.workflowConfig.fileName);
47
47
  cli_utilities_1.log.debug(`Writing workflows to: ${workflowsFilePath}`, this.exportConfig.context);
48
48
  utils_1.fsUtil.writeFile(workflowsFilePath, this.workflows);
49
- cli_utilities_1.log.success(cli_utilities_1.messageHandler.parse('WORKFLOW_EXPORT_COMPLETE', Object.keys(this.workflows || {}).length), this.exportConfig.context);
50
49
  }
51
- this.completeProgress(true);
50
+ this.completeProgressWithMessage();
52
51
  }
53
52
  catch (error) {
54
53
  (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.exportConfig.context));
@@ -140,7 +139,7 @@ class ExportWorkFlows extends base_class_1.default {
140
139
  .catch((err) => {
141
140
  cli_utilities_1.log.debug(`Failed to fetch role data for UID: ${roleUid}`, this.exportConfig.context);
142
141
  (0, cli_utilities_1.handleAndLogError)(err, Object.assign({}, this.exportConfig.context));
143
- throw err;
142
+ return undefined; // Return undefined instead of throwing to handle gracefully
144
143
  });
145
144
  }
146
145
  }
@@ -147,6 +147,12 @@ export default interface DefaultConfig {
147
147
  fileName: string;
148
148
  dependencies?: Modules[];
149
149
  };
150
+ 'composable-studio': {
151
+ dirName: string;
152
+ fileName: string;
153
+ apiBaseUrl: string;
154
+ apiVersion: string;
155
+ };
150
156
  masterLocale: {
151
157
  dirName: string;
152
158
  fileName: string;
@@ -26,7 +26,7 @@ export interface Region {
26
26
  cda: string;
27
27
  uiHost: string;
28
28
  }
29
- export type Modules = 'stack' | 'assets' | 'locales' | 'environments' | 'extensions' | 'webhooks' | 'global-fields' | 'entries' | 'content-types' | 'custom-roles' | 'workflows' | 'labels' | 'marketplace-apps' | 'taxonomies' | 'personalize';
29
+ export type Modules = 'stack' | 'assets' | 'locales' | 'environments' | 'extensions' | 'webhooks' | 'global-fields' | 'entries' | 'content-types' | 'custom-roles' | 'workflows' | 'labels' | 'marketplace-apps' | 'taxonomies' | 'personalize' | 'composable-studio';
30
30
  export type ModuleClassParams = {
31
31
  stackAPIClient: ReturnType<ContentstackClient['stack']>;
32
32
  exportConfig: ExportConfig;
@@ -96,11 +96,36 @@ export interface StackConfig {
96
96
  dependencies?: Modules[];
97
97
  limit?: number;
98
98
  }
99
+ export interface ComposableStudioConfig {
100
+ dirName: string;
101
+ fileName: string;
102
+ apiBaseUrl: string;
103
+ apiVersion: string;
104
+ }
105
+ export interface ComposableStudioProject {
106
+ name: string;
107
+ description: string;
108
+ canvasUrl: string;
109
+ connectedStackApiKey: string;
110
+ contentTypeUid: string;
111
+ organizationUid: string;
112
+ settings: {
113
+ configuration: {
114
+ environment: string;
115
+ locale: string;
116
+ };
117
+ };
118
+ createdBy: string;
119
+ updatedBy: string;
120
+ deletedAt: boolean;
121
+ createdAt: string;
122
+ updatedAt: string;
123
+ uid: string;
124
+ }
99
125
  export interface Context {
100
126
  command: string;
101
127
  module: string;
102
128
  userId: string | undefined;
103
- email?: string | undefined;
104
129
  sessionId: string | undefined;
105
130
  clientId?: string | undefined;
106
131
  apiKey: string;
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * Contentstack Import
3
- * Copyright (c) 2024 Contentstack LLC
3
+ * Copyright (c) 2026 Contentstack LLC
4
4
  * MIT Licensed
5
5
  */
6
6
  import { ExternalConfig } from '../types';
@@ -4,7 +4,7 @@
4
4
  /* eslint-disable no-empty */
5
5
  /*!
6
6
  * Contentstack Import
7
- * Copyright (c) 2024 Contentstack LLC
7
+ * Copyright (c) 2026 Contentstack LLC
8
8
  * MIT Licensed
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -16,7 +16,7 @@ const login = async (config) => {
16
16
  const response = await client.login({ email: config.email, password: config.password }).catch(Promise.reject);
17
17
  if ((_a = response === null || response === void 0 ? void 0 : response.user) === null || _a === void 0 ? void 0 : _a.authtoken) {
18
18
  config.headers = {
19
- api_key: config.source_stack,
19
+ api_key: config.apiKey,
20
20
  access_token: config.access_token,
21
21
  authtoken: response.user.authtoken,
22
22
  'X-User-Agent': 'contentstack-export/v',
@@ -26,15 +26,15 @@ const login = async (config) => {
26
26
  return config;
27
27
  }
28
28
  else {
29
- cli_utilities_1.log.error(`Failed to login, Invalid credentials`, config.context);
29
+ cli_utilities_1.log.error(`Failed to log in!`, config.context);
30
30
  process.exit(1);
31
31
  }
32
32
  }
33
- else if (!config.email && !config.password && config.source_stack && config.access_token) {
33
+ else if (!config.email && !config.password && config.apiKey && config.access_token) {
34
34
  cli_utilities_1.log.info(`Content types, entries, assets, labels, global fields, extensions modules will be exported`, config.context);
35
35
  cli_utilities_1.log.info(`Email, password, or management token is not set in the config, cannot export Webhook and label modules`, config.context);
36
36
  config.headers = {
37
- api_key: config.source_stack,
37
+ api_key: config.apiKey,
38
38
  access_token: config.access_token,
39
39
  'X-User-Agent': 'contentstack-export/v',
40
40
  };
@@ -1,6 +1,6 @@
1
1
  /*!
2
2
  * Contentstack Export
3
- * Copyright (c) 2024 Contentstack LLC
3
+ * Copyright (c) 2026 Contentstack LLC
4
4
  * MIT Licensed
5
5
  */
6
6
  import { ExternalConfig } from '../types';