@contentstack/cli-cm-import 1.25.1 → 1.26.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 (36) hide show
  1. package/README.md +3 -3
  2. package/lib/commands/cm/stacks/import.d.ts +1 -0
  3. package/lib/commands/cm/stacks/import.js +39 -12
  4. package/lib/import/module-importer.js +20 -3
  5. package/lib/import/modules/assets.d.ts +1 -1
  6. package/lib/import/modules/assets.js +93 -39
  7. package/lib/import/modules/content-types.js +76 -31
  8. package/lib/import/modules/custom-roles.js +95 -19
  9. package/lib/import/modules/entries.js +128 -57
  10. package/lib/import/modules/environments.js +48 -14
  11. package/lib/import/modules/extensions.js +78 -16
  12. package/lib/import/modules/global-fields.js +85 -20
  13. package/lib/import/modules/labels.d.ts +4 -4
  14. package/lib/import/modules/labels.js +60 -18
  15. package/lib/import/modules/locales.js +63 -20
  16. package/lib/import/modules/marketplace-apps.js +160 -31
  17. package/lib/import/modules/personalize.js +33 -7
  18. package/lib/import/modules/stack.js +5 -0
  19. package/lib/import/modules/taxonomies.js +52 -13
  20. package/lib/import/modules/variant-entries.js +21 -3
  21. package/lib/import/modules/webhooks.js +44 -12
  22. package/lib/import/modules/workflows.js +65 -21
  23. package/lib/types/import-config.d.ts +3 -1
  24. package/lib/types/index.d.ts +22 -0
  25. package/lib/utils/asset-helper.js +24 -1
  26. package/lib/utils/backup-handler.js +15 -1
  27. package/lib/utils/common-helper.js +41 -16
  28. package/lib/utils/content-type-helper.js +35 -2
  29. package/lib/utils/entries-helper.js +24 -2
  30. package/lib/utils/extension-helper.js +35 -1
  31. package/lib/utils/import-config-handler.js +21 -0
  32. package/lib/utils/login-handler.js +8 -4
  33. package/lib/utils/marketplace-app-helper.js +50 -11
  34. package/lib/utils/taxonomies-helper.js +22 -4
  35. package/oclif.manifest.json +2 -2
  36. package/package.json +6 -6
package/README.md CHANGED
@@ -47,7 +47,7 @@ $ npm install -g @contentstack/cli-cm-import
47
47
  $ csdx COMMAND
48
48
  running command...
49
49
  $ csdx (--version)
50
- @contentstack/cli-cm-import/1.25.1 linux-x64 node-v22.17.0
50
+ @contentstack/cli-cm-import/1.26.1 linux-x64 node-v22.17.1
51
51
  $ csdx --help [COMMAND]
52
52
  USAGE
53
53
  $ csdx COMMAND
@@ -89,7 +89,7 @@ FLAGS
89
89
  specified, the import command will import all the modules into the stack. The
90
90
  available modules are assets, content-types, entries, environments,
91
91
  extensions, marketplace-apps, global-fields, labels, locales, webhooks,
92
- workflows, custom-roles, and taxonomies.
92
+ workflows, custom-roles, personalize projects, and taxonomies.
93
93
  -y, --yes [optional] Force override all Marketplace prompts.
94
94
  --exclude-global-modules Excludes the branch-independent module from the import operation.
95
95
  --import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
@@ -155,7 +155,7 @@ FLAGS
155
155
  specified, the import command will import all the modules into the stack. The
156
156
  available modules are assets, content-types, entries, environments,
157
157
  extensions, marketplace-apps, global-fields, labels, locales, webhooks,
158
- workflows, custom-roles, and taxonomies.
158
+ workflows, custom-roles, personalize projects, and taxonomies.
159
159
  -y, --yes [optional] Force override all Marketplace prompts.
160
160
  --exclude-global-modules Excludes the branch-independent module from the import operation.
161
161
  --import-webhook-status=<option> [default: disable] [default: disable] (optional) This webhook state keeps the
@@ -7,4 +7,5 @@ export default class ImportCommand extends Command {
7
7
  static aliases: string[];
8
8
  static usage: string;
9
9
  run(): Promise<void>;
10
+ private createImportContext;
10
11
  }
@@ -1,7 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
3
  const cli_command_1 = require("@contentstack/cli-command");
6
4
  const cli_utilities_1 = require("@contentstack/cli-utilities");
7
5
  const import_1 = require("../../../import");
@@ -15,6 +13,10 @@ class ImportCommand extends cli_command_1.Command {
15
13
  try {
16
14
  const { flags } = await this.parse(ImportCommand);
17
15
  let importConfig = await (0, utils_1.setupImportConfig)(flags);
16
+ // Prepare the context object
17
+ const context = this.createImportContext(importConfig.apiKey, importConfig.authenticationMethod);
18
+ importConfig.context = Object.assign({}, context);
19
+ //log.info(`Using Cli Version: ${this.context?.cliVersion}`, importConfig.context);
18
20
  // Note setting host to create cma client
19
21
  importConfig.host = this.cmaHost;
20
22
  importConfig.region = this.region;
@@ -22,12 +24,15 @@ class ImportCommand extends cli_command_1.Command {
22
24
  importConfig.developerHubBaseUrl = this.developerHubUrl;
23
25
  if (this.personalizeUrl)
24
26
  importConfig.modules.personalize.baseURL[importConfig.region.name] = this.personalizeUrl;
25
- backupDir = importConfig.cliLogsPath || importConfig.backupDir;
26
27
  const managementAPIClient = await (0, cli_utilities_1.managementSDKClient)(importConfig);
27
28
  if (!flags.branch) {
28
29
  try {
30
+ // Use stack configuration to check for branch availability
31
+ // false positive - no hardcoded secret here
32
+ // @ts-ignore-next-line secret-detection
33
+ const keyProp = 'api_key';
29
34
  const branches = await managementAPIClient
30
- .stack({ api_key: importConfig.apiKey })
35
+ .stack({ [keyProp]: importConfig.apiKey })
31
36
  .branch()
32
37
  .query()
33
38
  .find()
@@ -40,22 +45,44 @@ class ImportCommand extends cli_command_1.Command {
40
45
  // Branch not enabled, just the let flow continue
41
46
  }
42
47
  }
48
+ // Set backupDir early so it's available in error handling
49
+ backupDir = importConfig.backupDir;
43
50
  const moduleImporter = new import_1.ModuleImporter(managementAPIClient, importConfig);
44
51
  const result = await moduleImporter.start();
45
52
  if (!(result === null || result === void 0 ? void 0 : result.noSuccessMsg)) {
46
- (0, utils_1.log)(importConfig, importConfig.stackName
53
+ const successMessage = importConfig.stackName
47
54
  ? `Successfully imported the content to the stack named ${importConfig.stackName} with the API key ${importConfig.apiKey} .`
48
- : `The content has been imported to the stack ${importConfig.apiKey} successfully!`, 'success');
55
+ : `The content has been imported to the stack ${importConfig.apiKey} successfully!`;
56
+ cli_utilities_1.log.success(successMessage, importConfig.context);
49
57
  }
50
- (0, utils_1.log)(importConfig, `The log has been stored at '${(0, cli_utilities_1.pathValidator)(node_path_1.default.join(importConfig.cliLogsPath || importConfig.backupDir, 'logs', 'import'))}'`, 'success');
58
+ cli_utilities_1.log.success(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`, importConfig.context);
59
+ cli_utilities_1.log.info(`The backup content has been stored at '${backupDir}'`, importConfig.context);
51
60
  }
52
61
  catch (error) {
53
- (0, utils_1.log)({ data: backupDir !== null && backupDir !== void 0 ? backupDir : (0, cli_utilities_1.pathValidator)(node_path_1.default.join(backupDir || __dirname, 'logs', 'import')) }, `Failed to import stack content - ${(0, utils_1.formatError)(error)}`, 'error');
54
- (0, utils_1.log)({ data: backupDir }, `The log has been stored at ${{ data: backupDir }
55
- ? (0, cli_utilities_1.pathValidator)(node_path_1.default.join(backupDir || __dirname, 'logs', 'import'))
56
- : (0, cli_utilities_1.pathValidator)(node_path_1.default.join(__dirname, 'logs'))}`, 'info');
62
+ (0, cli_utilities_1.handleAndLogError)(error);
63
+ cli_utilities_1.log.info(`The log has been stored at '${(0, cli_utilities_1.getLogPath)()}'`);
64
+ if (backupDir) {
65
+ cli_utilities_1.log.info(`The backup content has been stored at '${backupDir}'`);
66
+ }
67
+ else {
68
+ cli_utilities_1.log.info('No backup directory was created due to early termination');
69
+ }
57
70
  }
58
71
  }
72
+ // Create export context object
73
+ createImportContext(apiKey, authenticationMethod) {
74
+ var _a, _b, _c;
75
+ return {
76
+ command: ((_b = (_a = this.context) === null || _a === void 0 ? void 0 : _a.info) === null || _b === void 0 ? void 0 : _b.command) || 'cm:stacks:import',
77
+ module: '',
78
+ userId: cli_utilities_1.configHandler.get('userUid') || '',
79
+ email: cli_utilities_1.configHandler.get('email') || '',
80
+ sessionId: (_c = this.context) === null || _c === void 0 ? void 0 : _c.sessionId,
81
+ apiKey: apiKey || '',
82
+ orgId: cli_utilities_1.configHandler.get('oauthOrgUid') || '',
83
+ authenticationMethod: authenticationMethod || 'Basic Auth',
84
+ };
85
+ }
59
86
  }
60
87
  exports.default = ImportCommand;
61
88
  ImportCommand.description = cli_utilities_1.messageHandler.parse('Import content from a stack');
@@ -111,7 +138,7 @@ ImportCommand.flags = {
111
138
  module: cli_utilities_1.flags.string({
112
139
  required: false,
113
140
  char: 'm',
114
- description: '[optional] Specify the module to import into the target stack. If not specified, the import command will import all the modules into the stack. The available modules are assets, content-types, entries, environments, extensions, marketplace-apps, global-fields, labels, locales, webhooks, workflows, custom-roles, and taxonomies.',
141
+ description: '[optional] Specify the module to import into the target stack. If not specified, the import command will import all the modules into the stack. The available modules are assets, content-types, entries, environments, extensions, marketplace-apps, global-fields, labels, locales, webhooks, workflows, custom-roles, personalize projects, and taxonomies.',
115
142
  parse: (0, cli_utilities_1.printFlagDeprecation)(['-m'], ['--module']),
116
143
  }),
117
144
  'backup-dir': cli_utilities_1.flags.string({
@@ -40,7 +40,15 @@ class ModuleImporter {
40
40
  // NOTE audit and fix the import content.
41
41
  if (!this.importConfig.skipAudit &&
42
42
  (!this.importConfig.moduleName ||
43
- ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles'].includes(this.importConfig.moduleName))) {
43
+ [
44
+ 'content-types',
45
+ 'global-fields',
46
+ 'entries',
47
+ 'extensions',
48
+ 'workflows',
49
+ 'custom-roles',
50
+ 'assets'
51
+ ].includes(this.importConfig.moduleName))) {
44
52
  if (!(await this.auditImportData(logger))) {
45
53
  return { noSuccessMsg: true };
46
54
  }
@@ -50,7 +58,7 @@ class ModuleImporter {
50
58
  this.importConfig['master_locale'] = { code: masterLocalResponse.code };
51
59
  this.importConfig.masterLocale = { code: masterLocalResponse.code };
52
60
  }
53
- await (0, utils_1.sanitizeStack)(this.stackAPIClient);
61
+ await (0, utils_1.sanitizeStack)(this.importConfig);
54
62
  return this.import();
55
63
  }
56
64
  async import() {
@@ -119,11 +127,20 @@ class ModuleImporter {
119
127
  }
120
128
  else if (this.importConfig.modules.types.length) {
121
129
  this.importConfig.modules.types
122
- .filter((val) => ['content-types', 'global-fields', 'entries', 'extensions', 'workflows', 'custom-roles'].includes(val))
130
+ .filter((val) => [
131
+ 'content-types',
132
+ 'global-fields',
133
+ 'entries',
134
+ 'extensions',
135
+ 'workflows',
136
+ 'custom-roles',
137
+ 'assets'
138
+ ].includes(val))
123
139
  .forEach((val) => {
124
140
  args.push('--modules', val);
125
141
  });
126
142
  }
143
+ args.push('--modules', 'field-rules');
127
144
  (0, utils_1.log)(this.importConfig, 'Starting audit process', 'info');
128
145
  const result = await cli_audit_1.AuditFix.run(args);
129
146
  (0, utils_1.log)(this.importConfig, 'Audit process completed', 'info');
@@ -1,5 +1,5 @@
1
- import BaseClass, { ApiOptions } from './base-class';
2
1
  import { ModuleClassParams } from '../../types';
2
+ import BaseClass, { ApiOptions } from './base-class';
3
3
  export default class ImportAssets extends BaseClass {
4
4
  private fs;
5
5
  private assetsPath;
@@ -24,6 +24,7 @@ class ImportAssets extends base_class_1.default {
24
24
  this.assetsUidMap = {};
25
25
  this.assetsUrlMap = {};
26
26
  this.assetsFolderMap = {};
27
+ this.importConfig.context.module = 'assets';
27
28
  this.assetsPath = (0, node_path_1.join)(this.importConfig.backupDir, 'assets');
28
29
  this.mapperDirPath = (0, node_path_1.join)(this.importConfig.backupDir, 'mapper', 'assets');
29
30
  this.assetUidMapperPath = (0, node_path_1.join)(this.mapperDirPath, 'uid-mapping.json');
@@ -38,52 +39,76 @@ class ImportAssets extends base_class_1.default {
38
39
  * @returns {Promise<void>} Promise<any>
39
40
  */
40
41
  async start() {
41
- // NOTE Step 1: Import folders and create uid mapping file
42
- await this.importFolders();
43
- // NOTE Step 2: Import versioned assets and create it mapping files (uid, url)
44
- if (this.assetConfig.includeVersionedAssets) {
45
- if ((0, node_fs_1.existsSync)(`${this.assetsPath}/versions`))
46
- await this.importAssets(true);
47
- else
48
- (0, utils_1.log)(this.importConfig, 'No Versioned assets found to import', 'info');
42
+ try {
43
+ // NOTE Step 1: Import folders and create uid mapping file
44
+ cli_utilities_1.log.debug('Starting folder import process', this.importConfig.context);
45
+ await this.importFolders();
46
+ // NOTE Step 2: Import versioned assets and create it mapping files (uid, url)
47
+ if (this.assetConfig.includeVersionedAssets) {
48
+ const versionsPath = `${this.assetsPath}/versions`;
49
+ if ((0, node_fs_1.existsSync)(versionsPath)) {
50
+ cli_utilities_1.log.debug('Starting versioned assets import', this.importConfig.context);
51
+ await this.importAssets(true);
52
+ }
53
+ else {
54
+ cli_utilities_1.log.info('No Versioned assets found to import', this.importConfig.context);
55
+ }
56
+ }
57
+ // NOTE Step 3: Import Assets and create it mapping files (uid, url)
58
+ cli_utilities_1.log.debug('Starting assets import', this.importConfig.context);
59
+ await this.importAssets();
60
+ // NOTE Step 4: Publish assets
61
+ if (!this.importConfig.skipAssetsPublish) {
62
+ cli_utilities_1.log.debug('Starting assets publishing', this.importConfig.context);
63
+ await this.publish();
64
+ }
65
+ cli_utilities_1.log.success('Assets imported successfully!', this.importConfig.context);
66
+ }
67
+ catch (error) {
68
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
49
69
  }
50
- // NOTE Step 3: Import Assets and create it mapping files (uid, url)
51
- await this.importAssets();
52
- // NOTE Step 4: Publish assets
53
- if (!this.importConfig.skipAssetsPublish)
54
- await this.publish();
55
70
  }
56
71
  /**
57
72
  * @method importFolders
58
73
  * @returns {Promise<any>} Promise<any>
59
74
  */
60
75
  async importFolders() {
61
- const folders = this.fs.readFile((0, node_path_1.resolve)(this.assetsRootPath, 'folders.json'));
76
+ const foldersPath = (0, node_path_1.resolve)(this.assetsRootPath, 'folders.json');
77
+ cli_utilities_1.log.debug(`Reading folders from: ${foldersPath}`, this.importConfig.context);
78
+ const folders = this.fs.readFile(foldersPath);
62
79
  if ((0, isEmpty_1.default)(folders)) {
63
- (0, utils_1.log)(this.importConfig, 'No folders found to import', 'info');
80
+ cli_utilities_1.log.info('No folders found to import', this.importConfig.context);
64
81
  return;
65
82
  }
83
+ cli_utilities_1.log.debug(`Found ${folders.length} folders to import`, this.importConfig.context);
66
84
  const batches = this.constructFolderImportOrder(folders);
85
+ cli_utilities_1.log.debug(`Organized folders into ${batches.length} batches for import`, this.importConfig.context);
67
86
  const onSuccess = ({ response, apiData: { uid, name } = { uid: null, name: '' } }) => {
68
87
  this.assetsFolderMap[uid] = response.uid;
69
- (0, utils_1.log)(this.importConfig, `Created folder: '${name}'`, 'success');
88
+ cli_utilities_1.log.debug(`Created folder: ${name} (Mapped ${uid} → ${response.uid})`, this.importConfig.context);
89
+ cli_utilities_1.log.success(`Created folder: '${name}'`, this.importConfig.context);
70
90
  };
71
91
  const onReject = ({ error, apiData: { name } = { name: '' } }) => {
72
- (0, utils_1.log)(this.importConfig, `${name} folder creation failed.!`, 'error');
73
- (0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
92
+ cli_utilities_1.log.error(`${name} folder creation failed.!`, this.importConfig.context);
93
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { name }));
74
94
  };
75
95
  const serializeData = (apiOptions) => {
76
96
  if (apiOptions.apiData.parent_uid) {
97
+ const originalParent = apiOptions.apiData.parent_uid;
77
98
  apiOptions.apiData.parent_uid = this.assetsFolderMap[apiOptions.apiData.parent_uid];
99
+ cli_utilities_1.log.debug(`Mapped parent folder: ${originalParent} → ${apiOptions.apiData.parent_uid}`, this.importConfig.context);
78
100
  }
79
101
  return apiOptions;
80
102
  };
81
103
  const batch = (0, map_1.default)((0, unionBy_1.default)(batches, 'parent_uid'), 'parent_uid');
104
+ cli_utilities_1.log.debug(`Processing ${batch.length} folder batches`, this.importConfig.context);
82
105
  for (const parent_uid of batch) {
106
+ const currentBatch = (0, filter_1.default)(batches, { parent_uid });
107
+ cli_utilities_1.log.debug(`Processing batch with parent_uid: ${parent_uid} (${currentBatch.length} folders)`, this.importConfig.context);
83
108
  // NOTE create parent folders
84
109
  /* eslint-disable no-await-in-loop */
85
110
  await this.makeConcurrentCall({
86
- apiContent: (0, orderBy_1.default)((0, filter_1.default)(batches, { parent_uid }), 'created_at'),
111
+ apiContent: (0, orderBy_1.default)(currentBatch, 'created_at'),
87
112
  processName: 'import assets folders',
88
113
  apiParams: {
89
114
  serializeData,
@@ -96,6 +121,7 @@ class ImportAssets extends base_class_1.default {
96
121
  }, undefined, false);
97
122
  }
98
123
  if (!(0, isEmpty_1.default)(this.assetsFolderMap)) {
124
+ cli_utilities_1.log.debug(`Writing folder mappings to ${this.assetFolderUidMapperPath}`, this.importConfig.context);
99
125
  this.fs.writeFile(this.assetFolderUidMapperPath, this.assetsFolderMap);
100
126
  }
101
127
  }
@@ -108,32 +134,38 @@ class ImportAssets extends base_class_1.default {
108
134
  const processName = isVersion ? 'import versioned assets' : 'import assets';
109
135
  const indexFileName = isVersion ? 'versioned-assets.json' : 'assets.json';
110
136
  const basePath = isVersion ? (0, node_path_1.join)(this.assetsPath, 'versions') : this.assetsPath;
137
+ cli_utilities_1.log.debug(`Importing ${processName} from ${basePath}`, this.importConfig.context);
111
138
  const fs = new cli_utilities_1.FsUtility({ basePath, indexFileName });
112
139
  const indexer = fs.indexFileContent;
113
140
  const indexerCount = (0, values_1.default)(indexer).length;
141
+ cli_utilities_1.log.debug(`Found ${indexerCount} asset chunks to process`, this.importConfig.context);
114
142
  const onSuccess = ({ response = {}, apiData: { uid, url, title } = undefined }) => {
115
143
  this.assetsUidMap[uid] = response.uid;
116
144
  this.assetsUrlMap[url] = response.url;
117
- (0, utils_1.log)(this.importConfig, `Created asset: '${title}'`, 'info');
145
+ cli_utilities_1.log.debug(`Created asset: ${title} (Mapped ${uid} → ${response.uid})`, this.importConfig.context);
146
+ cli_utilities_1.log.success(`Created asset: '${title}'`, this.importConfig.context);
118
147
  };
119
148
  const onReject = ({ error, apiData: { title } = undefined }) => {
120
- (0, utils_1.log)(this.importConfig, `${title} asset upload failed.!`, 'error');
121
- (0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
149
+ cli_utilities_1.log.error(`${title} asset upload failed.!`, this.importConfig.context);
150
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { title }));
122
151
  };
123
152
  /* eslint-disable @typescript-eslint/no-unused-vars, guard-for-in */
124
153
  for (const index in indexer) {
154
+ cli_utilities_1.log.debug(`Processing chunk ${index} of ${indexerCount}`, this.importConfig.context);
125
155
  const chunk = await fs.readChunkFiles.next().catch((error) => {
126
- (0, utils_1.log)(this.importConfig, error, 'error');
156
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign({}, this.importConfig.context));
127
157
  });
128
158
  if (chunk) {
129
159
  let apiContent = (0, orderBy_1.default)((0, values_1.default)(chunk), '_version');
160
+ cli_utilities_1.log.debug(`Processing ${apiContent.length} assets in chunk`, this.importConfig.context);
130
161
  if (isVersion && this.assetConfig.importSameStructure) {
131
- // NOTE to create same structure it must have seed assets/version 1 asset to be created first
162
+ cli_utilities_1.log.debug('Processing version 1 assets first', this.importConfig.context);
163
+ const versionOneAssets = (0, filter_1.default)(apiContent, ({ _version }) => _version === 1);
132
164
  await this.makeConcurrentCall({
133
165
  processName,
134
166
  indexerCount,
135
167
  currentIndexer: +index,
136
- apiContent: (0, filter_1.default)(apiContent, ({ _version }) => _version === 1),
168
+ apiContent: versionOneAssets,
137
169
  apiParams: {
138
170
  reject: onReject,
139
171
  resolve: onSuccess,
@@ -144,6 +176,7 @@ class ImportAssets extends base_class_1.default {
144
176
  concurrencyLimit: this.assetConfig.uploadAssetsConcurrency,
145
177
  });
146
178
  apiContent = (0, filter_1.default)(apiContent, ({ _version }) => _version > 1);
179
+ cli_utilities_1.log.debug(`Processing ${apiContent.length} versioned assets after version 1`, this.importConfig.context);
147
180
  }
148
181
  await this.makeConcurrentCall({
149
182
  apiContent,
@@ -161,9 +194,17 @@ class ImportAssets extends base_class_1.default {
161
194
  }, undefined, !isVersion);
162
195
  }
163
196
  }
164
- if (!isVersion && (!(0, isEmpty_1.default)(this.assetsUidMap) || !(0, isEmpty_1.default)(this.assetsUrlMap))) {
165
- this.fs.writeFile(this.assetUidMapperPath, this.assetsUidMap);
166
- this.fs.writeFile(this.assetUrlMapperPath, this.assetsUrlMap);
197
+ if (!isVersion) {
198
+ if (!(0, isEmpty_1.default)(this.assetsUidMap)) {
199
+ const uidMappingCount = Object.keys(this.assetsUidMap).length;
200
+ cli_utilities_1.log.debug(`Writing ${uidMappingCount} UID mappings`, this.importConfig.context);
201
+ this.fs.writeFile(this.assetUidMapperPath, this.assetsUidMap);
202
+ }
203
+ if (!(0, isEmpty_1.default)(this.assetsUrlMap)) {
204
+ const urlMappingCount = Object.keys(this.assetsUrlMap).length;
205
+ cli_utilities_1.log.debug(`Writing ${urlMappingCount} URL mappings`, this.importConfig.context);
206
+ this.fs.writeFile(this.assetUrlMapperPath, this.assetsUrlMap);
207
+ }
167
208
  }
168
209
  }
169
210
  /**
@@ -175,24 +216,27 @@ class ImportAssets extends base_class_1.default {
175
216
  const { apiData: asset } = apiOptions;
176
217
  if (!this.assetConfig.importSameStructure &&
177
218
  !this.assetConfig.includeVersionedAssets &&
178
- /* eslint-disable @typescript-eslint/no-unused-vars, no-prototype-builtins */
179
219
  this.assetsUidMap.hasOwnProperty(asset.uid)) {
180
- (0, utils_1.log)(this.importConfig, `Skipping upload of asset: ${asset.uid}. Its mapped to: ${this.assetsUidMap[asset.uid]}`, 'success');
220
+ cli_utilities_1.log.info(`Skipping existing asset: ${asset.uid} (${asset.title})`, this.importConfig.context);
181
221
  apiOptions.entity = undefined;
182
222
  return apiOptions;
183
223
  }
184
224
  asset.upload = (0, node_path_1.join)(this.assetsPath, 'files', asset.uid, asset.filename);
225
+ cli_utilities_1.log.debug(`Asset file path resolved to: ${asset.upload}`, this.importConfig.context);
185
226
  if (asset.parent_uid) {
227
+ const originalParent = asset.parent_uid;
186
228
  asset.parent_uid = this.assetsFolderMap[asset.parent_uid];
229
+ cli_utilities_1.log.debug(`Mapped parent UID: ${originalParent} → ${asset.parent_uid}`, this.importConfig.context);
187
230
  }
188
231
  else if (this.importConfig.replaceExisting) {
189
- // adds the root folder as parent for all assets in the root level
190
232
  asset.parent_uid = this.assetsFolderMap[this.rootFolder.uid];
233
+ cli_utilities_1.log.debug(`Assigned root folder as parent: ${asset.parent_uid}`, this.importConfig.context);
191
234
  }
192
235
  apiOptions.apiData = asset;
193
236
  if (this.assetsUidMap[asset.uid] && this.assetConfig.importSameStructure) {
194
237
  apiOptions.entity = 'replace-assets';
195
238
  apiOptions.uid = this.assetsUidMap[asset.uid];
239
+ cli_utilities_1.log.debug(`Preparing to replace asset: ${asset.uid} → ${apiOptions.uid}`, this.importConfig.context);
196
240
  }
197
241
  return apiOptions;
198
242
  }
@@ -203,16 +247,18 @@ class ImportAssets extends base_class_1.default {
203
247
  async publish() {
204
248
  const fs = new cli_utilities_1.FsUtility({ basePath: this.assetsPath, indexFileName: 'assets.json' });
205
249
  if ((0, isEmpty_1.default)(this.assetsUidMap)) {
250
+ cli_utilities_1.log.debug('Loading asset UID mappings from file', this.importConfig.context);
206
251
  this.assetsUidMap = fs.readFile(this.assetUidMapperPath, true);
207
252
  }
208
253
  const indexer = fs.indexFileContent;
209
254
  const indexerCount = (0, values_1.default)(indexer).length;
255
+ cli_utilities_1.log.debug(`Found ${indexerCount} asset chunks to publish`, this.importConfig.context);
210
256
  const onSuccess = ({ apiData: { uid, title } = undefined }) => {
211
- (0, utils_1.log)(this.importConfig, `Asset '${uid}: ${title}' published successfully`, 'success');
257
+ cli_utilities_1.log.success(`Asset '${uid}: ${title}' published successfully`, this.importConfig.context);
212
258
  };
213
259
  const onReject = ({ error, apiData: { uid, title } = undefined }) => {
214
- (0, utils_1.log)(this.importConfig, `Asset '${uid}: ${title}' not published`, 'error');
215
- (0, utils_1.log)(this.importConfig, (0, utils_1.formatError)(error), 'error');
260
+ cli_utilities_1.log.error(`Asset '${uid}: ${title}' not published`, this.importConfig.context);
261
+ (0, cli_utilities_1.handleAndLogError)(error, Object.assign(Object.assign({}, this.importConfig.context), { uid, title }));
216
262
  };
217
263
  const serializeData = (apiOptions) => {
218
264
  const { apiData: asset } = apiOptions;
@@ -224,21 +270,26 @@ class ImportAssets extends base_class_1.default {
224
270
  const environments = (0, uniq_1.default)((0, map_1.default)(publishDetails, ({ environment }) => this.environments[environment].name));
225
271
  const locales = (0, uniq_1.default)((0, map_1.default)(publishDetails, 'locale'));
226
272
  if (environments.length === 0 || locales.length === 0) {
273
+ cli_utilities_1.log.debug(`Skipping publish for asset ${asset.uid} - no valid environments/locales`, this.importConfig.context);
227
274
  apiOptions.entity = undefined;
228
275
  return apiOptions;
229
276
  }
230
277
  asset.locales = locales;
231
278
  asset.environments = environments;
232
279
  apiOptions.apiData.publishDetails = { locales, environments };
280
+ cli_utilities_1.log.debug(`Prepared publish details for asset ${asset.uid}`, this.importConfig.context);
233
281
  }
234
282
  apiOptions.uid = this.assetsUidMap[asset.uid];
235
- if (!apiOptions.uid)
283
+ if (!apiOptions.uid) {
284
+ cli_utilities_1.log.debug(`Skipping publish for asset ${asset.uid} - no UID mapping found`, this.importConfig.context);
236
285
  apiOptions.entity = undefined;
286
+ }
237
287
  return apiOptions;
238
288
  };
239
- /* eslint-disable @typescript-eslint/no-unused-vars */
240
289
  for (const index in indexer) {
290
+ cli_utilities_1.log.debug(`Processing publish chunk ${index} of ${indexerCount}`, this.importConfig.context);
241
291
  const apiContent = (0, filter_1.default)((0, values_1.default)(await fs.readChunkFiles.next()), ({ publish_details }) => !(0, isEmpty_1.default)(publish_details));
292
+ cli_utilities_1.log.debug(`Found ${apiContent.length} publishable assets in chunk`, this.importConfig.context);
242
293
  await this.makeConcurrentCall({
243
294
  apiContent,
244
295
  indexerCount,
@@ -262,19 +313,22 @@ class ImportAssets extends base_class_1.default {
262
313
  */
263
314
  constructFolderImportOrder(folders) {
264
315
  let parentUIds = [];
265
- // NOTE: Read root folder
266
316
  const importOrder = (0, filter_1.default)(folders, { parent_uid: null }).map(({ uid, name, parent_uid, created_at }) => {
267
317
  parentUIds.push(uid);
268
318
  return { uid, name, parent_uid, created_at };
269
319
  });
320
+ cli_utilities_1.log.debug(`Found ${importOrder.length} root folders`, this.importConfig.context);
270
321
  while (!(0, isEmpty_1.default)(parentUIds)) {
271
322
  // NOTE: Read nested folders every iteration until we find empty folders
272
- parentUIds = (0, filter_1.default)(folders, ({ parent_uid }) => (0, includes_1.default)(parentUIds, parent_uid)).map(({ uid, name, parent_uid, created_at }) => {
323
+ const nestedFolders = (0, filter_1.default)(folders, ({ parent_uid }) => (0, includes_1.default)(parentUIds, parent_uid));
324
+ cli_utilities_1.log.debug(`Processing ${nestedFolders.length} nested folders`, this.importConfig.context);
325
+ parentUIds = nestedFolders.map(({ uid, name, parent_uid, created_at }) => {
273
326
  importOrder.push({ uid, name, parent_uid, created_at });
274
327
  return uid;
275
328
  });
276
329
  }
277
330
  if (this.importConfig.replaceExisting) {
331
+ cli_utilities_1.log.debug('Setting up root folder for import', this.importConfig.context);
278
332
  // Note: adds a root folder to distinguish latest asset uploads
279
333
  // Todo: This temporary approach should be updated with asset and folder overwrite strategy, which follows
280
334
  // folder overwrite
@@ -295,8 +349,8 @@ class ImportAssets extends base_class_1.default {
295
349
  importOrder.splice(index, 1, Object.assign(Object.assign({}, folder), { parent_uid: this.rootFolder.uid }));
296
350
  }
297
351
  });
298
- // NOTE: Adds root folder
299
352
  importOrder.unshift(this.rootFolder);
353
+ cli_utilities_1.log.debug('Added root folder to import order', this.importConfig.context);
300
354
  }
301
355
  return importOrder;
302
356
  }