mcdev 7.0.2 → 7.0.3

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 (41) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
  2. package/@types/lib/index.d.ts.map +1 -1
  3. package/@types/lib/metadataTypes/Asset.d.ts +11 -10
  4. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  5. package/@types/lib/metadataTypes/Automation.d.ts +0 -6
  6. package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
  7. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  8. package/@types/lib/metadataTypes/Journey.d.ts +14 -8
  9. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  10. package/@types/lib/metadataTypes/MetadataType.d.ts +2 -2
  11. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  12. package/@types/lib/util/init.config.d.ts +20 -4
  13. package/@types/lib/util/init.config.d.ts.map +1 -1
  14. package/@types/lib/util/init.npm.d.ts.map +1 -1
  15. package/@types/lib/util/replaceContentBlockReference.d.ts +7 -6
  16. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
  17. package/boilerplate/files/eslint.config.js +82 -0
  18. package/boilerplate/forcedUpdates.json +5 -0
  19. package/boilerplate/npm-dependencies.json +2 -0
  20. package/eslint.config.js +143 -0
  21. package/lib/index.js +5 -2
  22. package/lib/metadataTypes/Asset.js +11 -5
  23. package/lib/metadataTypes/Automation.js +27 -46
  24. package/lib/metadataTypes/DataExtension.js +9 -7
  25. package/lib/metadataTypes/Journey.js +2 -5
  26. package/lib/metadataTypes/MetadataType.js +6 -1
  27. package/lib/metadataTypes/definitions/Automation.definition.js +1 -0
  28. package/lib/util/init.config.js +79 -32
  29. package/lib/util/init.npm.js +8 -0
  30. package/lib/util/replaceContentBlockReference.js +2 -2
  31. package/package.json +5 -3
  32. package/test/mockRoot/.mcdevrc.json +1 -1
  33. package/test/resources/9999999/automation/create-callout-expected.json +66 -0
  34. package/test/resources/9999999/automation/update-callout-expected.json +68 -0
  35. package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +7 -7
  36. package/test/type.automation.test.js +46 -0
  37. package/test/utils.js +28 -3
  38. package/.eslintignore +0 -3
  39. package/.eslintrc.json +0 -116
  40. package/boilerplate/files/.eslintignore +0 -5
  41. package/boilerplate/files/.eslintrc +0 -37
@@ -117,10 +117,11 @@ class Asset extends MetadataType {
117
117
  *
118
118
  * @param {void | string[]} [_] parameter not used
119
119
  * @param {string[]} [subTypeArr] optionally limit to a single subtype
120
+ * @param {void | string} [__] parameter not used
120
121
  * @param {boolean} [loadShared] optionally retrieve assets from other BUs that were shared with the current
121
122
  * @returns {Promise.<{metadata: AssetMap, type: string}>} Promise
122
123
  */
123
- static retrieveForCache(_, subTypeArr, loadShared = false) {
124
+ static retrieveForCache(_, subTypeArr, __, loadShared = false) {
124
125
  return this.retrieve(null, null, subTypeArr, undefined, loadShared);
125
126
  }
126
127
 
@@ -1010,6 +1011,11 @@ class Asset extends MetadataType {
1010
1011
  * @param {MetadataTypeItem} metadata a single item
1011
1012
  */
1012
1013
  static setFolderId(metadata) {
1014
+ if (!metadata.r__folder_Path) {
1015
+ throw new Error(
1016
+ `Dependent folder could not be found because r__folder_Path is not set`
1017
+ );
1018
+ }
1013
1019
  metadata.category = {
1014
1020
  id: cache.searchForField('folder', metadata.r__folder_Path, 'Path', 'ID'),
1015
1021
  };
@@ -1158,7 +1164,7 @@ class Asset extends MetadataType {
1158
1164
  readDirArr = [deployDir, ...subDirArr, templateFileName];
1159
1165
  const fileName = 'content' + subtypeExtension;
1160
1166
 
1161
- const fileExtArr = ['html']; // eslint-disable-line no-case-declarations
1167
+ const fileExtArr = ['html'];
1162
1168
  for (const ext of fileExtArr) {
1163
1169
  if (
1164
1170
  await File.pathExists(
@@ -1327,7 +1333,7 @@ class Asset extends MetadataType {
1327
1333
  // metadata.content
1328
1334
  subDirArr = [this.definition.type, subType];
1329
1335
  readDirArr = [deployDir, ...subDirArr];
1330
- const fileExtArr = ['html', 'ssjs', 'amp']; // eslint-disable-line no-case-declarations
1336
+ const fileExtArr = ['html', 'ssjs', 'amp'];
1331
1337
  for (const ext of fileExtArr) {
1332
1338
  if (
1333
1339
  await File.pathExists(
@@ -1510,7 +1516,7 @@ class Asset extends MetadataType {
1510
1516
  case 'template': {
1511
1517
  // template-template
1512
1518
  // metadata.content
1513
- const fileExt = 'html'; // eslint-disable-line no-case-declarations
1519
+ const fileExt = 'html';
1514
1520
  if (metadata?.content?.length) {
1515
1521
  codeArr.push({
1516
1522
  subFolder: null,
@@ -2116,7 +2122,7 @@ class Asset extends MetadataType {
2116
2122
  * @param {string} subType asset subtype
2117
2123
  * @param {object} item api response for metadata
2118
2124
  * @param {string} buName owner business unit name
2119
- * @returns {string} path to the asset's code
2125
+ * @returns {Promise.<string>} path to the asset's code
2120
2126
  */
2121
2127
  static async #getPath(subType, item, buName) {
2122
2128
  const pathBase1 = `./retrieve/${this.buObject.credential}/${buName}/${this.definition.type}/${subType}/${item[this.definition.keyField]}.${this.definition.type}-${subType}-meta.`;
@@ -506,6 +506,8 @@ class Automation extends MetadataType {
506
506
  );
507
507
  }
508
508
  }
509
+ // In some cases the displayOrder and array order are not equal which leads to a different order every time we retrieve & deployed the automation. To prevent that, we sort the activities by displayOrder on retrieve
510
+ step.activities.sort((a, b) => a.displayOrder - b.displayOrder);
509
511
  }
510
512
  }
511
513
  return structuredClone(metadata);
@@ -846,25 +848,25 @@ class Automation extends MetadataType {
846
848
  for (const activity of step.activities) {
847
849
  activity.displayOrder = ++displayOrder;
848
850
  if (
849
- activity.name &&
851
+ activity.r__key &&
850
852
  this.definition.dependencies.includes(activity.r__type)
851
853
  ) {
852
854
  if (
853
855
  activity.r__type === 'verification' &&
854
- this.createdKeyMap?.[buName]?.verification?.[activity.name]
856
+ this.createdKeyMap?.[buName]?.verification?.[activity.r__key]
855
857
  ) {
856
- Util.logger.info(
858
+ Util.logger.debug(
857
859
  Util.getGrayMsg(
858
860
  ` - updated verification activity name from ${
859
- activity.name
861
+ activity.r__key
860
862
  } to ${
861
- this.createdKeyMap[buName].verification[activity.name]
863
+ this.createdKeyMap[buName].verification[activity.r__key]
862
864
  }`
863
865
  )
864
866
  );
865
867
  // map structure: cred/bu --> type --> old key --> new key
866
- activity.name =
867
- this.createdKeyMap[buName].verification[activity.name];
868
+ activity.r__key =
869
+ this.createdKeyMap[buName].verification[activity.r__key];
868
870
  }
869
871
  // automations can have empty placeholder for activities with only their type defined
870
872
  activity.activityObjectId = cache.searchForField(
@@ -873,9 +875,16 @@ class Automation extends MetadataType {
873
875
  Definitions[activity.r__type].keyField,
874
876
  Definitions[activity.r__type].idField
875
877
  );
878
+ activity.name = cache.searchForField(
879
+ activity.r__type,
880
+ activity.r__key,
881
+ Definitions[activity.r__type].keyField,
882
+ Definitions[activity.r__type].nameField
883
+ );
876
884
  }
877
885
  activity.objectTypeId =
878
886
  this.definition.activityTypeMapping[activity.r__type];
887
+ delete activity.r__key;
879
888
  delete activity.r__type;
880
889
  }
881
890
  step.annotation = step.name;
@@ -911,13 +920,13 @@ class Automation extends MetadataType {
911
920
  // check if manual deploy required. if so then log warning
912
921
  if (this.definition.manualDeployTypes.includes(activity.r__type)) {
913
922
  Util.logger.warn(
914
- `- ${this.definition.type} '${metadata.name}' requires additional manual configuration: '${activity.name}' in step ${stepNumber}.${displayOrder}`
923
+ `- ${this.definition.type} '${metadata.name}' requires additional manual configuration: '${activity.r__key || activity.name}' in step ${stepNumber}.${displayOrder}`
915
924
  );
916
925
  }
917
926
  // cannot deploy because it is not supported
918
927
  else if (!this.definition.dependencies.includes(activity.r__type)) {
919
928
  errors.push(
920
- ` • not supported ${activity.r__type} activity '${activity.name}' in step ${stepNumber}.${displayOrder}`
929
+ ` • not supported ${activity.r__type} activity '${activity.r__key || activity.name}' in step ${stepNumber}.${displayOrder}`
921
930
  );
922
931
  deployable = false;
923
932
  }
@@ -1169,36 +1178,6 @@ class Automation extends MetadataType {
1169
1178
  }
1170
1179
  }
1171
1180
 
1172
- /**
1173
- * automation-specific script that retrieves the folder ID from cache and updates the given metadata with it before deploy
1174
- *
1175
- * @param {MetadataTypeItem} metadata a single item
1176
- */
1177
- static setFolderId(metadata) {
1178
- try {
1179
- metadata.categoryId = cache.searchForField(
1180
- 'folder',
1181
- metadata.r__folder_Path,
1182
- 'Path',
1183
- 'ID'
1184
- );
1185
- if (metadata.r__folder_Path !== 'my automations') {
1186
- Util.logger.warn(
1187
- ` - Automation '${
1188
- metadata[this.definition.nameField]
1189
- }' is located in subfolder ${
1190
- metadata.r__folder_Path
1191
- }. Please note that creating automation folders is not supported via API and hence you will have to create it manually in the GUI if you choose to deploy this automation.`
1192
- );
1193
- }
1194
- delete metadata.r__folder_Path;
1195
- } catch {
1196
- throw new Error(
1197
- `Folder '${metadata.r__folder_Path}' was not found on the server. Please create this manually in the GUI. Automation-folders cannot be deployed automatically.`
1198
- );
1199
- }
1200
- }
1201
-
1202
1181
  /**
1203
1182
  * Builds a schedule object to be used for scheduling an automation
1204
1183
  * based on combination of ical string and start/end dates.
@@ -1543,13 +1522,15 @@ class Automation extends MetadataType {
1543
1522
  static async document(metadata) {
1544
1523
  if (['md', 'both'].includes(this.properties.options.documentType)) {
1545
1524
  if (!metadata) {
1546
- metadata = await this.readBUMetadataForType(
1547
- File.normalizePath([
1548
- this.properties.directories.retrieve,
1549
- this.buObject.credential,
1550
- this.buObject.businessUnit,
1551
- ]),
1552
- true
1525
+ metadata = (
1526
+ await this.readBUMetadataForType(
1527
+ File.normalizePath([
1528
+ this.properties.directories.retrieve,
1529
+ this.buObject.credential,
1530
+ this.buObject.businessUnit,
1531
+ ]),
1532
+ true
1533
+ )
1553
1534
  ).automation;
1554
1535
  }
1555
1536
  const docPath = File.normalizePath([
@@ -1353,13 +1353,15 @@ class DataExtension extends MetadataType {
1353
1353
  static async document(metadataMap) {
1354
1354
  try {
1355
1355
  if (!metadataMap) {
1356
- metadataMap = await this.readBUMetadataForType(
1357
- File.normalizePath([
1358
- this.properties.directories.retrieve,
1359
- this.buObject.credential,
1360
- this.buObject.businessUnit,
1361
- ]),
1362
- true
1356
+ metadataMap = (
1357
+ await this.readBUMetadataForType(
1358
+ File.normalizePath([
1359
+ this.properties.directories.retrieve,
1360
+ this.buObject.credential,
1361
+ this.buObject.businessUnit,
1362
+ ]),
1363
+ true
1364
+ )
1363
1365
  ).dataExtension;
1364
1366
  }
1365
1367
  } catch (ex) {
@@ -49,8 +49,6 @@ class Journey extends MetadataType {
49
49
  let singleKey = '';
50
50
  let mode = 'key';
51
51
  if (key) {
52
- /* eslint-disable unicorn/prefer-ternary */
53
-
54
52
  if (key.startsWith('id:') || key.startsWith('%23')) {
55
53
  // ! allow selecting journeys by ID because that's what users see in the URL
56
54
  // if the key started with %23 assume an ID was copied from the URL but the user forgot to prefix it with id:
@@ -71,7 +69,6 @@ class Journey extends MetadataType {
71
69
  // assume actual key was provided
72
70
  singleKey = 'key:' + encodeURIComponent(key);
73
71
  }
74
- /* eslint-enable unicorn/prefer-ternary */
75
72
  }
76
73
 
77
74
  try {
@@ -176,7 +173,7 @@ class Journey extends MetadataType {
176
173
  static async deleteByKey(key) {
177
174
  let version;
178
175
  let singleKey = '';
179
- /* eslint-disable unicorn/prefer-ternary */
176
+
180
177
  if (key.startsWith('id:') || key.startsWith('%23')) {
181
178
  // ! allow selecting journeys by ID because that's what users see in the URL
182
179
  // if the key started with %23 assume an ID was copied from the URL but the user forgot to prefix it with id:
@@ -223,7 +220,7 @@ class Journey extends MetadataType {
223
220
  Util.logger.warn(
224
221
  `Deleting Journeys via this command breaks following retrieve-by-key/id requests until you've deployed/created a new draft version! You can get still get the latest available version of your journey by retrieving all interactions on this BU.`
225
222
  );
226
- /* eslint-enable unicorn/prefer-ternary */
223
+
227
224
  return super.deleteByKeyREST(
228
225
  '/interaction/v1/interactions/' + singleKey + `?versionNumber=${version}`,
229
226
  key,
@@ -245,6 +245,11 @@ class MetadataType {
245
245
  if (!this.definition.folderIdField) {
246
246
  return;
247
247
  }
248
+ if (!metadata.r__folder_Path) {
249
+ throw new Error(
250
+ `Dependent folder could not be found because r__folder_Path is not set`
251
+ );
252
+ }
248
253
  metadata[this.definition.folderIdField] = cache.searchForField(
249
254
  'folder',
250
255
  metadata.r__folder_Path,
@@ -1831,7 +1836,7 @@ class MetadataType {
1831
1836
  * @param {string[]} baseDir [retrieveDir, ...overrideType.split('-')]
1832
1837
  * @param {string} [subtypeExtension] e.g. ".asset-meta" or ".query-meta"
1833
1838
  * @param {TemplateMap} [templateVariables] variables to be replaced in the metadata
1834
- * @returns {MetadataTypeItem} saved metadata
1839
+ * @returns {Promise.<MetadataTypeItem>} saved metadata
1835
1840
  */
1836
1841
  static async saveToDisk(results, originalKey, baseDir, subtypeExtension, templateVariables) {
1837
1842
  subtypeExtension ||= '.' + this.definition.type + '-meta';
@@ -528,6 +528,7 @@ export default {
528
528
  template: false,
529
529
  },
530
530
  'steps[].activities[].displayOrder': {
531
+ // we remove it during post-processing but need to ensure the array order equals the displayOrder
531
532
  isCreateable: true,
532
533
  isUpdateable: true,
533
534
  retrieving: false,
@@ -240,7 +240,9 @@ const Init = {
240
240
  const creationLog = [];
241
241
  await File.ensureDir('deploy/');
242
242
  await File.ensureDir('src/cloudPages');
243
- const relevantForcedUpdates = await this._getForcedUpdateList(versionBeforeUpgrade);
243
+ const relevantForced = await this._getForcedUpdateList(versionBeforeUpgrade);
244
+
245
+ await this._removeIdeConfigFiles(relevantForced);
244
246
 
245
247
  // copy in .gitignore (cant be retrieved via npm install directly)
246
248
  const gitignoreFileName = path.resolve(
@@ -253,7 +255,7 @@ const Init = {
253
255
  creationLog.push(
254
256
  await this._createIdeConfigFile(
255
257
  ['.' + path.sep, '', '.gitignore'],
256
- relevantForcedUpdates,
258
+ relevantForced,
257
259
  fileContent
258
260
  )
259
261
  );
@@ -280,7 +282,7 @@ const Init = {
280
282
  creationLog.push(
281
283
  await this._createIdeConfigFile(
282
284
  [subdir + path.sep, fileArr.join('.'), ext],
283
- relevantForcedUpdates
285
+ relevantForced
284
286
  )
285
287
  );
286
288
  }
@@ -328,12 +330,13 @@ const Init = {
328
330
  * returns list of files that need to be updated
329
331
  *
330
332
  * @param {string} projectVersion version found in config file of the current project
331
- * @returns {Promise.<string[]>} relevant files with path that need to be updated
333
+ * @returns {Promise.<{updates:string[],deletes:string[]}>} relevant files with path that need to be updated
332
334
  */
333
335
  async _getForcedUpdateList(projectVersion) {
334
336
  // list of files that absolutely need to get overwritten, no questions asked, when upgrading from a version lower than the given.
335
337
  let forceIdeConfigUpdate;
336
- const relevantForcedUpdates = [];
338
+ const updates = [];
339
+ const deletes = [];
337
340
  if (await File.pathExists(Util.configFileName)) {
338
341
  forceIdeConfigUpdate = await File.readJSON(
339
342
  path.resolve(__dirname, Util.boilerplateDirectory, 'forcedUpdates.json')
@@ -341,27 +344,33 @@ const Init = {
341
344
  // return all if no project version was found or only changes from "newer" versions otherwise
342
345
  for (const element of forceIdeConfigUpdate) {
343
346
  if (!projectVersion || semver.gt(element.version, projectVersion)) {
344
- relevantForcedUpdates.push(
347
+ updates.push(
345
348
  // adapt it for local file systems
346
349
  ...element.files.map((item) => path.normalize(item))
347
350
  );
351
+ if (element.filesRemove) {
352
+ deletes.push(
353
+ // adapt it for local file systems
354
+ ...element.filesRemove.map((item) => path.normalize(item))
355
+ );
356
+ }
348
357
  } else {
349
358
  continue;
350
359
  }
351
360
  }
352
361
  }
353
362
 
354
- return relevantForcedUpdates;
363
+ return { updates, deletes };
355
364
  },
356
365
  /**
357
366
  * handles creation/update of one config file from the boilerplate at a time
358
367
  *
359
368
  * @param {string[]} fileNameArr 0: path, 1: filename, 2: extension with dot
360
- * @param {string[]} relevantForcedUpdates if fileNameArr is in this list we require an override
369
+ * @param {{updates:string[],deletes:string[]}} relevantForced if fileNameArr is in this list we require an override
361
370
  * @param {string} [boilerplateFileContent] in case we cannot copy files 1:1 this can be used to pass in content
362
371
  * @returns {Promise.<boolean>} install successful or error occured
363
372
  */
364
- async _createIdeConfigFile(fileNameArr, relevantForcedUpdates, boilerplateFileContent) {
373
+ async _createIdeConfigFile(fileNameArr, relevantForced, boilerplateFileContent) {
365
374
  let update = false;
366
375
  const fileName = fileNameArr.join('');
367
376
  const boilerplateFileName = path.resolve(
@@ -372,16 +381,27 @@ const Init = {
372
381
  );
373
382
  boilerplateFileContent ||= await File.readFile(boilerplateFileName, 'utf8');
374
383
 
384
+ let todo = null;
385
+
375
386
  if (await File.pathExists(fileName)) {
376
- const existingFileContent = await File.readFile(fileName, 'utf8');
377
- if (existingFileContent === boilerplateFileContent) {
378
- Util.logger.info(`- ✔️ ${fileName} found. No update needed`);
379
- return true;
387
+ if (relevantForced.deletes.includes(path.normalize(fileName))) {
388
+ Util.logger.info(
389
+ `- ${fileName} found but it is required to delete it. Commencing rename instead for your convenience:`
390
+ );
391
+ todo = 'delete';
392
+ } else {
393
+ const existingFileContent = await File.readFile(fileName, 'utf8');
394
+ if (existingFileContent === boilerplateFileContent) {
395
+ Util.logger.info(`- ✔️ ${fileName} found. No update needed`);
396
+ return true;
397
+ }
380
398
  }
381
- if (relevantForcedUpdates.includes(path.normalize(fileName))) {
399
+
400
+ if (relevantForced.updates.includes(path.normalize(fileName))) {
382
401
  Util.logger.info(
383
402
  `- ✋ ${fileName} found but an update is required. Commencing with override:`
384
403
  );
404
+ todo = 'update';
385
405
  } else {
386
406
  Util.logger.info(
387
407
  `- ✋ ${fileName} found with differences to the new standard version. We recommend updating it.`
@@ -400,33 +420,60 @@ const Init = {
400
420
  return true;
401
421
  }
402
422
  }
423
+ update = true;
403
424
  }
404
- update = true;
405
425
 
406
426
  // ensure our update is not leading to data loss in case config files were not versioned correctly by the user
407
427
  await File.rename(fileName, fileName + '.BAK');
428
+ } else if (!relevantForced.deletes.includes(path.normalize(fileName))) {
429
+ todo = 'create';
408
430
  }
409
- const saveStatus = await File.writeToFile(
410
- fileNameArr[0],
411
- fileNameArr[1],
412
- fileNameArr[2].slice(1),
413
- boilerplateFileContent
414
- );
415
-
416
- if (saveStatus) {
417
- Util.logger.info(
418
- `- ✔️ ${fileName} ${
419
- update
420
- ? `updated (we created a backup of the old file under ${fileName + '.BAK'})`
421
- : 'created'
422
- }`
431
+ if (todo === 'create' || todo === 'update') {
432
+ const saveStatus = await File.writeToFile(
433
+ fileNameArr[0],
434
+ fileNameArr[1],
435
+ fileNameArr[2].slice(1),
436
+ boilerplateFileContent
423
437
  );
438
+
439
+ if (saveStatus) {
440
+ Util.logger.info(
441
+ `- ✔️ ${fileName} ${
442
+ update
443
+ ? `updated (we created a backup of the old file under ${fileName + '.BAK'})`
444
+ : 'created'
445
+ }`
446
+ );
447
+ return true;
448
+ } else {
449
+ Util.logger.warn(`- ❌ ${fileName} ${update ? 'update' : 'creation'} failed`);
450
+ return false;
451
+ }
452
+ } else if (todo === 'delete') {
453
+ await File.rename(fileName, fileName + '.BAK');
454
+ Util.logger.info(`- ✔️ ${fileName} removed (renamed to ${fileName + '.BAK'})`);
424
455
  return true;
425
- } else {
426
- Util.logger.warn(`- ❌ ${fileName} ${update ? 'update' : 'creation'} failed`);
427
- return false;
428
456
  }
429
457
  },
458
+ /**
459
+ * handles deletion of no longer needed config files
460
+ *
461
+ * @param {{updates:string[],deletes:string[]}} relevantForced if file is in .deletes, we require deleting/renaming it
462
+ * @returns {Promise.<boolean>} deletion successful or error occured
463
+ */
464
+ async _removeIdeConfigFiles(relevantForced) {
465
+ for (const fileName of relevantForced.deletes) {
466
+ if (await File.pathExists(fileName)) {
467
+ Util.logger.info(
468
+ `- ✋ ${fileName} found but it is required to delete it. Commencing rename instead for your convenience:`
469
+ );
470
+
471
+ await File.rename(fileName, fileName + '.BAK');
472
+ Util.logger.info(`- ✔️ ${fileName} removed (renamed to ${fileName + '.BAK'})`);
473
+ }
474
+ }
475
+ return true;
476
+ },
430
477
  /**
431
478
  * helper method for this.upgradeProject that upgrades project config if needed
432
479
  *
@@ -57,6 +57,8 @@ const Init = {
57
57
  if (fileContent) {
58
58
  projectPackageJson = JSON.parse(fileContent);
59
59
  }
60
+ // type="module" is solely required for the new flat configs of ESLint >=9
61
+ Util.execSync('npm', ['pkg', 'set', 'type="module"'], true);
60
62
  Util.logger.info('✔️ package.json created');
61
63
  } catch {
62
64
  Util.logger.error('No package.json found. Please run "npm init" manually');
@@ -105,6 +107,8 @@ const Init = {
105
107
  !projectPackageJson.devDependencies ||
106
108
  !projectPackageJson.devDependencies[name] ||
107
109
  versionsDefault[name] == 'latest' ||
110
+ // filters out file:.. references instead of versions that would otherwise lead to an error in semver.gt()
111
+ !semver.valid(versionsProject[name]) ||
108
112
  semver.gt(versionsDefault[name], versionsProject[name])
109
113
  );
110
114
  if (loadDependencies.length) {
@@ -152,6 +156,10 @@ const Init = {
152
156
  if (!currentContent.license || currentContent.license === 'ISC') {
153
157
  currentContent.license = 'UNLICENSED';
154
158
  }
159
+ if (!currentContent.type || currentContent.type !== 'module') {
160
+ // type="module" is solely required for the new flat configs of ESLint >=9
161
+ currentContent.type = 'module';
162
+ }
155
163
  return currentContent;
156
164
  },
157
165
  };
@@ -45,7 +45,7 @@ export default class ReplaceContentBlockReference {
45
45
  key: {},
46
46
  name: {},
47
47
  };
48
- /** @type {{id: RegExp[], key: RegExp[], name: RegExp[]}} */
48
+ /** @type {Object.<string, {id: RegExp[], key: RegExp[], name: RegExp[]}>} */
49
49
  static #regexBy = {
50
50
  // TODO: handle cases in which variables or functions are passed into ContentBlockByX
51
51
 
@@ -140,7 +140,6 @@ export default class ReplaceContentBlockReference {
140
140
  }
141
141
  /**
142
142
  *
143
- * @private
144
143
  * @param {ContentBlockConversionTypes} from replace with
145
144
  * @param {string|number} identifier id, key or name of asset
146
145
  * @param {string} parentName name of the object that was passed in; used in error message only
@@ -250,6 +249,7 @@ saved
250
249
  const resultAssetShared = await Asset.retrieveForCache(
251
250
  undefined,
252
251
  ['asset', 'code', 'textfile', 'block', 'other'],
252
+ undefined,
253
253
  true
254
254
  );
255
255
  for (const element of Object.values(resultAssetShared.metadata)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "7.0.2",
3
+ "version": "7.0.3",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -86,6 +86,7 @@
86
86
  "yargs": "17.7.2"
87
87
  },
88
88
  "devDependencies": {
89
+ "@eslint/js": "9.6.0",
89
90
  "@types/mocha": "10.0.6",
90
91
  "@types/node": "20.14.9",
91
92
  "assert": "2.1.0",
@@ -93,14 +94,15 @@
93
94
  "c8": "10.0.0",
94
95
  "chai": "5.1.1",
95
96
  "chai-files": "1.4.0",
96
- "eslint": "8.57.0",
97
+ "eslint": "9.6.0",
97
98
  "eslint-config-prettier": "9.1.0",
98
- "eslint-config-ssjs": "1.1.11",
99
+ "eslint-config-ssjs": "2.0.0",
99
100
  "eslint-plugin-jsdoc": "48.2.7",
100
101
  "eslint-plugin-mocha": "10.4.3",
101
102
  "eslint-plugin-prettier": "5.1.2",
102
103
  "eslint-plugin-unicorn": "53.0.0",
103
104
  "fast-xml-parser": "4.4.0",
105
+ "globals": "15.6.0",
104
106
  "husky": "9.0.11",
105
107
  "lint-staged": "15.2.7",
106
108
  "mocha": "10.4.0",
@@ -100,5 +100,5 @@
100
100
  "verification"
101
101
  ]
102
102
  },
103
- "version": "7.0.2"
103
+ "version": "7.0.3"
104
104
  }
@@ -0,0 +1,66 @@
1
+ {
2
+ "description": "created on deploy",
3
+ "key": "testNew_automation",
4
+ "name": "testNew_automation",
5
+ "status": "Scheduled",
6
+ "steps": [
7
+ {
8
+ "activities": [
9
+ {
10
+ "displayOrder": 1,
11
+ "activityObjectId": "56c5370a-f988-4f36-b0ee-0f876573f6d7",
12
+ "name": "testExisting_dataExtract",
13
+ "objectTypeId": 73
14
+ },
15
+ {
16
+ "displayOrder": 2,
17
+ "activityObjectId": "9b1c7bf9-4964-ed11-b849-48df37d1de8b",
18
+ "name": "testExisting_emailSend",
19
+ "objectTypeId": 42
20
+ },
21
+ {
22
+ "displayOrder": 3,
23
+ "activityObjectId": "72c328ac-f5b0-4e37-91d3-a775666f15a6",
24
+ "name": "testExisting_fileTransfer",
25
+ "objectTypeId": 53
26
+ },
27
+ {
28
+ "displayOrder": 4,
29
+ "activityObjectId": "9d16f42c-2260-ed11-b849-48df37d1de8b",
30
+ "name": "testExisting_importFile",
31
+ "objectTypeId": 43
32
+ },
33
+ {
34
+ "displayOrder": 5,
35
+ "activityObjectId": "549f0568-607c-4940-afef-437965094dat",
36
+ "name": "testExisting_query",
37
+ "objectTypeId": 300
38
+ },
39
+ {
40
+ "displayOrder": 6,
41
+ "activityObjectId": "39f6a488-20eb-4ba0-b0b9-023725b574e4",
42
+ "name": "testExisting_script",
43
+ "objectTypeId": 423
44
+ },
45
+ {
46
+ "displayOrder": 7,
47
+ "activityObjectId": "testNew_RANDOM_NEW_GUID",
48
+ "name": "testNew_RANDOM_NEW_GUID",
49
+ "objectTypeId": 1000
50
+ }
51
+ ],
52
+ "annotation": "",
53
+ "stepNumber": 0
54
+ }
55
+ ],
56
+ "categoryId": 290937,
57
+ "startSource": {
58
+ "schedule": {
59
+ "startDate": "2020-05-14T02:30:32.11",
60
+ "endDate": "2079-06-06T21:00:00",
61
+ "icalRecur": "FREQ=MINUTELY;UNTIL=20790607T050000;INTERVAL=5",
62
+ "timezoneId": 5
63
+ },
64
+ "typeId": 1
65
+ }
66
+ }