mcdev 6.0.2 → 7.0.0

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 (602) hide show
  1. package/.beautyamp.json +13 -0
  2. package/.eslintrc.json +7 -1
  3. package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
  4. package/.github/PULL_REQUEST_TEMPLATE/pr_template_release.md +10 -3
  5. package/.github/workflows/close_issues_on_merge.yml +1 -1
  6. package/.github/workflows/code-test.yml +51 -3
  7. package/.github/workflows/coverage-base-update.yml +1 -1
  8. package/.github/workflows/coverage-develop-branch.yml +1 -1
  9. package/.github/workflows/coverage-main-branch.yml +1 -1
  10. package/.github/workflows/coverage.yml +2 -2
  11. package/.github/workflows/npm-publish.yml +3 -2
  12. package/.github/workflows/pr-labeler.yml +1 -1
  13. package/.husky/commit-msg +0 -2
  14. package/.husky/post-checkout +0 -3
  15. package/.husky/post-merge +2 -7
  16. package/.husky/pre-commit +5 -5
  17. package/.prettierrc +0 -8
  18. package/.vscode/extensions.json +2 -0
  19. package/.vscode/settings.json +15 -3
  20. package/@types/lib/Builder.d.ts +97 -0
  21. package/@types/lib/Builder.d.ts.map +1 -0
  22. package/@types/lib/Deployer.d.ts +99 -0
  23. package/@types/lib/Deployer.d.ts.map +1 -0
  24. package/@types/lib/MetadataTypeDefinitions.d.ts +80 -0
  25. package/@types/lib/MetadataTypeDefinitions.d.ts.map +1 -0
  26. package/@types/lib/MetadataTypeInfo.d.ts +78 -0
  27. package/@types/lib/MetadataTypeInfo.d.ts.map +1 -0
  28. package/@types/lib/Retriever.d.ts +66 -0
  29. package/@types/lib/Retriever.d.ts.map +1 -0
  30. package/@types/lib/cli.d.ts +3 -0
  31. package/@types/lib/cli.d.ts.map +1 -0
  32. package/@types/lib/index.d.ts +383 -0
  33. package/@types/lib/index.d.ts.map +1 -0
  34. package/@types/lib/metadataTypes/Asset.d.ts +1082 -0
  35. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -0
  36. package/@types/lib/metadataTypes/AttributeGroup.d.ts +290 -0
  37. package/@types/lib/metadataTypes/AttributeGroup.d.ts.map +1 -0
  38. package/@types/lib/metadataTypes/AttributeSet.d.ts +968 -0
  39. package/@types/lib/metadataTypes/AttributeSet.d.ts.map +1 -0
  40. package/@types/lib/metadataTypes/Automation.d.ts +921 -0
  41. package/@types/lib/metadataTypes/Automation.d.ts.map +1 -0
  42. package/@types/lib/metadataTypes/Campaign.d.ts +190 -0
  43. package/@types/lib/metadataTypes/Campaign.d.ts.map +1 -0
  44. package/@types/lib/metadataTypes/ContentArea.d.ts +250 -0
  45. package/@types/lib/metadataTypes/ContentArea.d.ts.map +1 -0
  46. package/@types/lib/metadataTypes/DataExtension.d.ts +571 -0
  47. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -0
  48. package/@types/lib/metadataTypes/DataExtensionField.d.ts +236 -0
  49. package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -0
  50. package/@types/lib/metadataTypes/DataExtensionTemplate.d.ts +175 -0
  51. package/@types/lib/metadataTypes/DataExtensionTemplate.d.ts.map +1 -0
  52. package/@types/lib/metadataTypes/DataExtract.d.ts +217 -0
  53. package/@types/lib/metadataTypes/DataExtract.d.ts.map +1 -0
  54. package/@types/lib/metadataTypes/DataExtractType.d.ts +80 -0
  55. package/@types/lib/metadataTypes/DataExtractType.d.ts.map +1 -0
  56. package/@types/lib/metadataTypes/DeliveryProfile.d.ts +102 -0
  57. package/@types/lib/metadataTypes/DeliveryProfile.d.ts.map +1 -0
  58. package/@types/lib/metadataTypes/Discovery.d.ts +189 -0
  59. package/@types/lib/metadataTypes/Discovery.d.ts.map +1 -0
  60. package/@types/lib/metadataTypes/Email.d.ts +384 -0
  61. package/@types/lib/metadataTypes/Email.d.ts.map +1 -0
  62. package/@types/lib/metadataTypes/EmailSend.d.ts +652 -0
  63. package/@types/lib/metadataTypes/EmailSend.d.ts.map +1 -0
  64. package/@types/lib/metadataTypes/Event.d.ts +958 -0
  65. package/@types/lib/metadataTypes/Event.d.ts.map +1 -0
  66. package/@types/lib/metadataTypes/FileLocation.d.ts +101 -0
  67. package/@types/lib/metadataTypes/FileLocation.d.ts.map +1 -0
  68. package/@types/lib/metadataTypes/FileTransfer.d.ts +228 -0
  69. package/@types/lib/metadataTypes/FileTransfer.d.ts.map +1 -0
  70. package/@types/lib/metadataTypes/Filter.d.ts +145 -0
  71. package/@types/lib/metadataTypes/Filter.d.ts.map +1 -0
  72. package/@types/lib/metadataTypes/Folder.d.ts +287 -0
  73. package/@types/lib/metadataTypes/Folder.d.ts.map +1 -0
  74. package/@types/lib/metadataTypes/ImportFile.d.ts +409 -0
  75. package/@types/lib/metadataTypes/ImportFile.d.ts.map +1 -0
  76. package/@types/lib/metadataTypes/Journey.d.ts +541 -0
  77. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -0
  78. package/@types/lib/metadataTypes/List.d.ts +191 -0
  79. package/@types/lib/metadataTypes/List.d.ts.map +1 -0
  80. package/@types/lib/metadataTypes/MetadataType.d.ts +680 -0
  81. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -0
  82. package/@types/lib/metadataTypes/MobileCode.d.ts +225 -0
  83. package/@types/lib/metadataTypes/MobileCode.d.ts.map +1 -0
  84. package/@types/lib/metadataTypes/MobileKeyword.d.ts +432 -0
  85. package/@types/lib/metadataTypes/MobileKeyword.d.ts.map +1 -0
  86. package/@types/lib/metadataTypes/MobileMessage.d.ts +931 -0
  87. package/@types/lib/metadataTypes/MobileMessage.d.ts.map +1 -0
  88. package/@types/lib/metadataTypes/Query.d.ts +299 -0
  89. package/@types/lib/metadataTypes/Query.d.ts.map +1 -0
  90. package/@types/lib/metadataTypes/Role.d.ts +164 -0
  91. package/@types/lib/metadataTypes/Role.d.ts.map +1 -0
  92. package/@types/lib/metadataTypes/Script.d.ts +305 -0
  93. package/@types/lib/metadataTypes/Script.d.ts.map +1 -0
  94. package/@types/lib/metadataTypes/SendClassification.d.ts +221 -0
  95. package/@types/lib/metadataTypes/SendClassification.d.ts.map +1 -0
  96. package/@types/lib/metadataTypes/SenderProfile.d.ts +257 -0
  97. package/@types/lib/metadataTypes/SenderProfile.d.ts.map +1 -0
  98. package/@types/lib/metadataTypes/TransactionalEmail.d.ts +230 -0
  99. package/@types/lib/metadataTypes/TransactionalEmail.d.ts.map +1 -0
  100. package/@types/lib/metadataTypes/TransactionalMessage.d.ts +163 -0
  101. package/@types/lib/metadataTypes/TransactionalMessage.d.ts.map +1 -0
  102. package/@types/lib/metadataTypes/TransactionalPush.d.ts +175 -0
  103. package/@types/lib/metadataTypes/TransactionalPush.d.ts.map +1 -0
  104. package/@types/lib/metadataTypes/TransactionalSMS.d.ts +205 -0
  105. package/@types/lib/metadataTypes/TransactionalSMS.d.ts.map +1 -0
  106. package/@types/lib/metadataTypes/TriggeredSend.d.ts +671 -0
  107. package/@types/lib/metadataTypes/TriggeredSend.d.ts.map +1 -0
  108. package/@types/lib/metadataTypes/User.d.ts +677 -0
  109. package/@types/lib/metadataTypes/User.d.ts.map +1 -0
  110. package/@types/lib/metadataTypes/Verification.d.ts +188 -0
  111. package/@types/lib/metadataTypes/Verification.d.ts.map +1 -0
  112. package/@types/lib/metadataTypes/definitions/Asset.definition.d.ts +692 -0
  113. package/@types/lib/metadataTypes/definitions/Asset.definition.d.ts.map +1 -0
  114. package/@types/lib/metadataTypes/definitions/AttributeGroup.definition.d.ts +246 -0
  115. package/@types/lib/metadataTypes/definitions/AttributeGroup.definition.d.ts.map +1 -0
  116. package/@types/lib/metadataTypes/definitions/AttributeSet.definition.d.ts +893 -0
  117. package/@types/lib/metadataTypes/definitions/AttributeSet.definition.d.ts.map +1 -0
  118. package/@types/lib/metadataTypes/definitions/Automation.definition.d.ts +616 -0
  119. package/@types/lib/metadataTypes/definitions/Automation.definition.d.ts.map +1 -0
  120. package/@types/lib/metadataTypes/definitions/Campaign.definition.d.ts +126 -0
  121. package/@types/lib/metadataTypes/definitions/Campaign.definition.d.ts.map +1 -0
  122. package/@types/lib/metadataTypes/definitions/ContentArea.definition.d.ts +182 -0
  123. package/@types/lib/metadataTypes/definitions/ContentArea.definition.d.ts.map +1 -0
  124. package/@types/lib/metadataTypes/definitions/DataExtension.definition.d.ts +237 -0
  125. package/@types/lib/metadataTypes/definitions/DataExtension.definition.d.ts.map +1 -0
  126. package/@types/lib/metadataTypes/definitions/DataExtensionField.definition.d.ts +121 -0
  127. package/@types/lib/metadataTypes/definitions/DataExtensionField.definition.d.ts.map +1 -0
  128. package/@types/lib/metadataTypes/definitions/DataExtensionTemplate.definition.d.ts +133 -0
  129. package/@types/lib/metadataTypes/definitions/DataExtensionTemplate.definition.d.ts.map +1 -0
  130. package/@types/lib/metadataTypes/definitions/DataExtract.definition.d.ts +156 -0
  131. package/@types/lib/metadataTypes/definitions/DataExtract.definition.d.ts.map +1 -0
  132. package/@types/lib/metadataTypes/definitions/DataExtractType.definition.d.ts +34 -0
  133. package/@types/lib/metadataTypes/definitions/DataExtractType.definition.d.ts.map +1 -0
  134. package/@types/lib/metadataTypes/definitions/DeliveryProfile.definition.d.ts +80 -0
  135. package/@types/lib/metadataTypes/definitions/DeliveryProfile.definition.d.ts.map +1 -0
  136. package/@types/lib/metadataTypes/definitions/Discovery.definition.d.ts +146 -0
  137. package/@types/lib/metadataTypes/definitions/Discovery.definition.d.ts.map +1 -0
  138. package/@types/lib/metadataTypes/definitions/Email.definition.d.ts +314 -0
  139. package/@types/lib/metadataTypes/definitions/Email.definition.d.ts.map +1 -0
  140. package/@types/lib/metadataTypes/definitions/EmailSend.definition.d.ts +582 -0
  141. package/@types/lib/metadataTypes/definitions/EmailSend.definition.d.ts.map +1 -0
  142. package/@types/lib/metadataTypes/definitions/Event.definition.d.ts +867 -0
  143. package/@types/lib/metadataTypes/definitions/Event.definition.d.ts.map +1 -0
  144. package/@types/lib/metadataTypes/definitions/FileLocation.definition.d.ts +68 -0
  145. package/@types/lib/metadataTypes/definitions/FileLocation.definition.d.ts.map +1 -0
  146. package/@types/lib/metadataTypes/definitions/FileTransfer.definition.d.ts +191 -0
  147. package/@types/lib/metadataTypes/definitions/FileTransfer.definition.d.ts.map +1 -0
  148. package/@types/lib/metadataTypes/definitions/Filter.definition.d.ts +147 -0
  149. package/@types/lib/metadataTypes/definitions/Filter.definition.d.ts.map +1 -0
  150. package/@types/lib/metadataTypes/definitions/Folder.definition.d.ts +174 -0
  151. package/@types/lib/metadataTypes/definitions/Folder.definition.d.ts.map +1 -0
  152. package/@types/lib/metadataTypes/definitions/ImportFile.definition.d.ts +306 -0
  153. package/@types/lib/metadataTypes/definitions/ImportFile.definition.d.ts.map +1 -0
  154. package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts +426 -0
  155. package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts.map +1 -0
  156. package/@types/lib/metadataTypes/definitions/List.definition.d.ts +118 -0
  157. package/@types/lib/metadataTypes/definitions/List.definition.d.ts.map +1 -0
  158. package/@types/lib/metadataTypes/definitions/MobileCode.definition.d.ts +167 -0
  159. package/@types/lib/metadataTypes/definitions/MobileCode.definition.d.ts.map +1 -0
  160. package/@types/lib/metadataTypes/definitions/MobileKeyword.definition.d.ts +276 -0
  161. package/@types/lib/metadataTypes/definitions/MobileKeyword.definition.d.ts.map +1 -0
  162. package/@types/lib/metadataTypes/definitions/MobileMessage.definition.d.ts +783 -0
  163. package/@types/lib/metadataTypes/definitions/MobileMessage.definition.d.ts.map +1 -0
  164. package/@types/lib/metadataTypes/definitions/Query.definition.d.ts +203 -0
  165. package/@types/lib/metadataTypes/definitions/Query.definition.d.ts.map +1 -0
  166. package/@types/lib/metadataTypes/definitions/Role.definition.d.ts +108 -0
  167. package/@types/lib/metadataTypes/definitions/Role.definition.d.ts.map +1 -0
  168. package/@types/lib/metadataTypes/definitions/Script.definition.d.ts +153 -0
  169. package/@types/lib/metadataTypes/definitions/Script.definition.d.ts.map +1 -0
  170. package/@types/lib/metadataTypes/definitions/SendClassification.definition.d.ts +151 -0
  171. package/@types/lib/metadataTypes/definitions/SendClassification.definition.d.ts.map +1 -0
  172. package/@types/lib/metadataTypes/definitions/SenderProfile.definition.d.ts +180 -0
  173. package/@types/lib/metadataTypes/definitions/SenderProfile.definition.d.ts.map +1 -0
  174. package/@types/lib/metadataTypes/definitions/TransactionalEmail.definition.d.ts +167 -0
  175. package/@types/lib/metadataTypes/definitions/TransactionalEmail.definition.d.ts.map +1 -0
  176. package/@types/lib/metadataTypes/definitions/TransactionalMessage.definition.d.ts +99 -0
  177. package/@types/lib/metadataTypes/definitions/TransactionalMessage.definition.d.ts.map +1 -0
  178. package/@types/lib/metadataTypes/definitions/TransactionalPush.definition.d.ts +119 -0
  179. package/@types/lib/metadataTypes/definitions/TransactionalPush.definition.d.ts.map +1 -0
  180. package/@types/lib/metadataTypes/definitions/TransactionalSMS.definition.d.ts +113 -0
  181. package/@types/lib/metadataTypes/definitions/TransactionalSMS.definition.d.ts.map +1 -0
  182. package/@types/lib/metadataTypes/definitions/TriggeredSend.definition.d.ts +560 -0
  183. package/@types/lib/metadataTypes/definitions/TriggeredSend.definition.d.ts.map +1 -0
  184. package/@types/lib/metadataTypes/definitions/User.definition.d.ts +423 -0
  185. package/@types/lib/metadataTypes/definitions/User.definition.d.ts.map +1 -0
  186. package/@types/lib/metadataTypes/definitions/Verification.definition.d.ts +129 -0
  187. package/@types/lib/metadataTypes/definitions/Verification.definition.d.ts.map +1 -0
  188. package/@types/lib/retrieveChangelog.d.ts +3 -0
  189. package/@types/lib/retrieveChangelog.d.ts.map +1 -0
  190. package/@types/lib/util/auth.d.ts +41 -0
  191. package/@types/lib/util/auth.d.ts.map +1 -0
  192. package/@types/lib/util/businessUnit.d.ts +21 -0
  193. package/@types/lib/util/businessUnit.d.ts.map +1 -0
  194. package/@types/lib/util/cache.d.ts +64 -0
  195. package/@types/lib/util/cache.d.ts.map +1 -0
  196. package/@types/lib/util/cli.d.ts +125 -0
  197. package/@types/lib/util/cli.d.ts.map +1 -0
  198. package/@types/lib/util/config.d.ts +45 -0
  199. package/@types/lib/util/config.d.ts.map +1 -0
  200. package/@types/lib/util/devops.d.ts +62 -0
  201. package/@types/lib/util/devops.d.ts.map +1 -0
  202. package/@types/lib/util/file.d.ts +20 -0
  203. package/@types/lib/util/file.d.ts.map +1 -0
  204. package/@types/lib/util/init.config.d.ts +66 -0
  205. package/@types/lib/util/init.config.d.ts.map +1 -0
  206. package/@types/lib/util/init.d.ts +72 -0
  207. package/@types/lib/util/init.d.ts.map +1 -0
  208. package/@types/lib/util/init.git.d.ts +40 -0
  209. package/@types/lib/util/init.git.d.ts.map +1 -0
  210. package/@types/lib/util/init.npm.d.ts +24 -0
  211. package/@types/lib/util/init.npm.d.ts.map +1 -0
  212. package/@types/lib/util/replaceContentBlockReference.d.ts +105 -0
  213. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -0
  214. package/@types/lib/util/util.d.ts +322 -0
  215. package/@types/lib/util/util.d.ts.map +1 -0
  216. package/@types/types/mcdev.d.d.ts +1282 -0
  217. package/@types/types/mcdev.d.d.ts.map +1 -0
  218. package/LICENSE +1 -1
  219. package/README.md +7 -3
  220. package/boilerplate/files/.beautyamp.json +13 -0
  221. package/boilerplate/files/.prettierrc +3 -0
  222. package/boilerplate/files/.vscode/extensions.json +6 -1
  223. package/boilerplate/files/.vscode/settings.json +16 -4
  224. package/boilerplate/forcedUpdates.json +9 -0
  225. package/jsconfig.json +7 -2
  226. package/lib/Builder.js +49 -45
  227. package/lib/Deployer.js +100 -45
  228. package/lib/MetadataTypeDefinitions.js +6 -0
  229. package/lib/MetadataTypeInfo.js +4 -0
  230. package/lib/Retriever.js +33 -27
  231. package/lib/cli.js +363 -28
  232. package/lib/index.js +615 -55
  233. package/lib/metadataTypes/Asset.js +811 -159
  234. package/lib/metadataTypes/AttributeGroup.js +17 -12
  235. package/lib/metadataTypes/AttributeSet.js +20 -15
  236. package/lib/metadataTypes/Automation.js +125 -93
  237. package/lib/metadataTypes/Campaign.js +18 -6
  238. package/lib/metadataTypes/ContentArea.js +21 -18
  239. package/lib/metadataTypes/DataExtension.js +229 -113
  240. package/lib/metadataTypes/DataExtensionField.js +52 -53
  241. package/lib/metadataTypes/DataExtensionTemplate.js +17 -5
  242. package/lib/metadataTypes/DataExtract.js +62 -27
  243. package/lib/metadataTypes/DataExtractType.js +17 -5
  244. package/lib/metadataTypes/DeliveryProfile.js +47 -0
  245. package/lib/metadataTypes/Discovery.js +15 -4
  246. package/lib/metadataTypes/Email.js +22 -19
  247. package/lib/metadataTypes/EmailSend.js +135 -35
  248. package/lib/metadataTypes/Event.js +95 -60
  249. package/lib/metadataTypes/FileLocation.js +17 -5
  250. package/lib/metadataTypes/FileTransfer.js +62 -26
  251. package/lib/metadataTypes/Filter.js +16 -4
  252. package/lib/metadataTypes/Folder.js +67 -39
  253. package/lib/metadataTypes/ImportFile.js +341 -91
  254. package/lib/metadataTypes/Journey.js +681 -324
  255. package/lib/metadataTypes/List.js +26 -14
  256. package/lib/metadataTypes/MetadataType.js +501 -265
  257. package/lib/metadataTypes/MobileCode.js +17 -5
  258. package/lib/metadataTypes/MobileKeyword.js +59 -43
  259. package/lib/metadataTypes/MobileMessage.js +67 -40
  260. package/lib/metadataTypes/Query.js +46 -38
  261. package/lib/metadataTypes/Role.js +34 -23
  262. package/lib/metadataTypes/Script.js +127 -39
  263. package/lib/metadataTypes/SendClassification.js +120 -5
  264. package/lib/metadataTypes/SenderProfile.js +179 -0
  265. package/lib/metadataTypes/TransactionalEmail.js +30 -17
  266. package/lib/metadataTypes/TransactionalMessage.js +25 -11
  267. package/lib/metadataTypes/TransactionalPush.js +24 -9
  268. package/lib/metadataTypes/TransactionalSMS.js +78 -46
  269. package/lib/metadataTypes/TriggeredSend.js +191 -43
  270. package/lib/metadataTypes/User.js +90 -62
  271. package/lib/metadataTypes/Verification.js +39 -21
  272. package/lib/metadataTypes/definitions/Asset.definition.js +3 -3
  273. package/lib/metadataTypes/definitions/AttributeGroup.definition.js +7 -1
  274. package/lib/metadataTypes/definitions/AttributeSet.definition.js +2 -2
  275. package/lib/metadataTypes/definitions/Automation.definition.js +4 -1
  276. package/lib/metadataTypes/definitions/Campaign.definition.js +1 -1
  277. package/lib/metadataTypes/definitions/ContentArea.definition.js +1 -1
  278. package/lib/metadataTypes/definitions/DataExtension.definition.js +17 -1
  279. package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +1 -1
  280. package/lib/metadataTypes/definitions/DataExtractType.definition.js +2 -2
  281. package/lib/metadataTypes/definitions/DeliveryProfile.definition.js +59 -0
  282. package/lib/metadataTypes/definitions/Discovery.definition.js +1 -1
  283. package/lib/metadataTypes/definitions/Email.definition.js +1 -1
  284. package/lib/metadataTypes/definitions/EmailSend.definition.js +19 -4
  285. package/lib/metadataTypes/definitions/Event.definition.js +140 -128
  286. package/lib/metadataTypes/definitions/FileLocation.definition.js +1 -1
  287. package/lib/metadataTypes/definitions/Filter.definition.js +1 -1
  288. package/lib/metadataTypes/definitions/Folder.definition.js +25 -2
  289. package/lib/metadataTypes/definitions/ImportFile.definition.js +52 -7
  290. package/lib/metadataTypes/definitions/Journey.definition.js +12 -0
  291. package/lib/metadataTypes/definitions/List.definition.js +1 -1
  292. package/lib/metadataTypes/definitions/MobileCode.definition.js +1 -1
  293. package/lib/metadataTypes/definitions/MobileMessage.definition.js +11 -5
  294. package/lib/metadataTypes/definitions/Query.definition.js +6 -0
  295. package/lib/metadataTypes/definitions/Script.definition.js +14 -14
  296. package/lib/metadataTypes/definitions/SendClassification.definition.js +60 -20
  297. package/lib/metadataTypes/definitions/SenderProfile.definition.js +185 -0
  298. package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +2 -2
  299. package/lib/metadataTypes/definitions/TransactionalMessage.definition.js +69 -0
  300. package/lib/metadataTypes/definitions/TransactionalPush.definition.js +6 -0
  301. package/lib/metadataTypes/definitions/TransactionalSMS.definition.js +6 -0
  302. package/lib/metadataTypes/definitions/TriggeredSend.definition.js +15 -4
  303. package/lib/metadataTypes/definitions/Verification.definition.js +1 -1
  304. package/lib/retrieveChangelog.js +7 -2
  305. package/lib/util/auth.js +56 -21
  306. package/lib/util/businessUnit.js +20 -2
  307. package/lib/util/cache.js +37 -18
  308. package/lib/util/cli.js +72 -29
  309. package/lib/util/config.js +27 -9
  310. package/lib/util/devops.js +45 -24
  311. package/lib/util/file.js +58 -33
  312. package/lib/util/init.config.js +55 -24
  313. package/lib/util/init.git.js +6 -6
  314. package/lib/util/init.js +36 -14
  315. package/lib/util/init.npm.js +2 -2
  316. package/lib/util/replaceContentBlockReference.js +278 -0
  317. package/lib/util/util.js +152 -54
  318. package/package.json +33 -30
  319. package/test/general.test.js +1327 -4
  320. package/test/mockRoot/.mcdevrc.json +26 -4
  321. package/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json +3 -4
  322. package/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testNew_dataExtensionShared.dataExtension-meta.json +1 -4
  323. package/test/mockRoot/deploy/testInstance/_ParentBU_/query/testNew_query.query-meta.json +11 -0
  324. package/test/mockRoot/deploy/testInstance/_ParentBU_/query/testNew_query.query-meta.sql +4 -0
  325. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset.asset-block-meta.html +12 -0
  326. package/test/mockRoot/deploy/testInstance/testBU/asset/block/testNew_asset.asset-block-meta.json +39 -0
  327. package/test/mockRoot/deploy/testInstance/testBU/automation/testExisting_automation.automation-meta.json +6 -6
  328. package/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json +7 -7
  329. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json +3 -4
  330. package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testNew_dataExtension.dataExtension-meta.json +1 -4
  331. package/test/mockRoot/deploy/testInstance/testBU/emailSend/testExisting_emailSend.emailSend-meta.json +36 -0
  332. package/test/mockRoot/deploy/testInstance/testBU/emailSend/testNew_emailSend.emailSend-meta.json +36 -0
  333. package/test/mockRoot/deploy/testInstance/testBU/importFile/testExisting_importFile.importFile-meta.json +10 -4
  334. package/test/mockRoot/deploy/testInstance/testBU/importFile/testNew_importFile.importFile-meta.json +8 -4
  335. package/test/mockRoot/deploy/testInstance/testBU/journey/testExisting_journey_Quicksend.journey-meta.json +232 -0
  336. package/test/mockRoot/deploy/testInstance/testBU/mobileMessage/NTIzOjc4OjA.mobileMessage-meta.json +2 -4
  337. package/test/mockRoot/deploy/testInstance/testBU/mobileMessage/new.mobileMessage-meta.json +2 -4
  338. package/test/mockRoot/deploy/testInstance/testBU/query/testExisting_query.query-meta.json +1 -1
  339. package/test/mockRoot/deploy/testInstance/testBU/query/testExisting_query_fixKeys.query-meta.json +1 -1
  340. package/test/mockRoot/deploy/testInstance/testBU/query/testNew_query.query-meta.json +1 -1
  341. package/test/mockRoot/deploy/testInstance/testBU/sendClassification/testExisting_sendClassification.sendClassification-meta.json +8 -0
  342. package/test/mockRoot/deploy/testInstance/testBU/sendClassification/testNew_sendClassification.sendClassification-meta.json +8 -0
  343. package/test/mockRoot/deploy/testInstance/testBU/senderProfile/testExisting_senderProfile.senderProfile-meta.json +14 -0
  344. package/test/mockRoot/deploy/testInstance/testBU/senderProfile/testNew_senderProfile.senderProfile-meta.json +14 -0
  345. package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +1 -1
  346. package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json +1 -1
  347. package/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json +1 -3
  348. package/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json +1 -3
  349. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json +1 -2
  350. package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json +1 -2
  351. package/test/mockRoot/deploy/testInstance/testBU/triggeredSend/testExisting_triggeredSend.triggeredSend-meta.json +4 -5
  352. package/test/mockRoot/deploy/testInstance/testBU/triggeredSend/testNew_triggeredSend.triggeredSend-meta.json +4 -5
  353. package/test/mockRoot/deploy/testInstance/testBU/verification/testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json +1 -1
  354. package/test/mockRoot/deploy/testInstance/testBU/verification/testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json +1 -1
  355. package/test/resourceFactory.js +118 -7
  356. package/test/resources/1111111/automation/v1/queries/get-response.json +6 -0
  357. package/test/resources/1111111/automation/v1/queries/post-response.json +18 -0
  358. package/test/resources/1111111/dataExtension/create-expected.json +4 -7
  359. package/test/resources/1111111/dataExtension/retrieve-expected.json +2 -4
  360. package/test/resources/1111111/dataExtension/update-expected.json +7 -7
  361. package/test/resources/1111111/dataExtension/update-response.xml +3 -1
  362. package/test/resources/1111111/dataFolder/retrieve-ContentType=queryactivity-response.xml +48 -0
  363. package/test/resources/1111111/query/patch_keySuffix-expected.json +11 -0
  364. package/test/resources/1111111/query/patch_keySuffix-expected.sql +4 -0
  365. package/test/resources/9999999/asset/block-1157-retrieve-expected.html +4 -3
  366. package/test/resources/9999999/asset/build-templatebasedemail-expected.json +81 -0
  367. package/test/resources/9999999/asset/build-templatebasedemail-html-expected.html +20 -0
  368. package/test/resources/9999999/asset/build-templatebasedemail-preheader-expected.amp +1 -0
  369. package/test/resources/9999999/asset/create-expected.json +18 -0
  370. package/test/resources/9999999/asset/resolveId-1234-notFound-expected.json +1 -0
  371. package/test/resources/9999999/asset/resolveId-1295064-noPath-expected.json +8 -0
  372. package/test/resources/9999999/asset/resolveId-1295064-withPath-expected.json +8 -0
  373. package/test/resources/9999999/asset/retrieve-templatebasedemail-expected.json +84 -0
  374. package/test/resources/9999999/asset/retrieve-templatebasedemail-html-expected.html +20 -0
  375. package/test/resources/9999999/asset/retrieve-templatebasedemail-preheader-expected.amp +1 -0
  376. package/test/resources/9999999/asset/template-templatebasedemail-expected.json +81 -0
  377. package/test/resources/9999999/asset/template-templatebasedemail-html-expected.html +20 -0
  378. package/test/resources/9999999/asset/template-templatebasedemail-preheader-expected.amp +1 -0
  379. package/test/resources/9999999/asset/testExisting_asset_message-html-rcb-id-expected.html +33 -0
  380. package/test/resources/9999999/asset/testExisting_asset_message-html-rcb-key-expected.html +33 -0
  381. package/test/resources/9999999/asset/testExisting_asset_message-html-rcb-name-expected.html +35 -0
  382. package/test/resources/9999999/asset/testExisting_asset_message-preheader-rcb-id-expected.amp +4 -0
  383. package/test/resources/9999999/asset/testExisting_asset_message-preheader-rcb-key-expected.amp +4 -0
  384. package/test/resources/9999999/asset/testExisting_asset_message-preheader-rcb-name-expected.amp +4 -0
  385. package/test/resources/9999999/asset/testExisting_asset_message-text-rcb-id-expected.amp +4 -0
  386. package/test/resources/9999999/asset/testExisting_asset_message-text-rcb-key-expected.amp +4 -0
  387. package/test/resources/9999999/asset/testExisting_asset_message-text-rcb-name-expected.amp +4 -0
  388. package/test/resources/9999999/asset/v1/content/assets/1295064/get-response.json +1 -1
  389. package/test/resources/9999999/asset/v1/content/assets/5286/get-response.json +48 -0
  390. package/test/resources/9999999/asset/v1/content/assets/5289/get-response.json +198 -0
  391. package/test/resources/9999999/asset/v1/content/assets/808714/get-response.json +4 -3
  392. package/test/resources/9999999/asset/v1/content/assets/950143/delete-response.txt +1 -0
  393. package/test/resources/9999999/asset/v1/content/assets/get-response-customerKey=testExisting_asset.json +105 -0
  394. package/test/resources/9999999/asset/v1/content/assets/post-response.json +59 -0
  395. package/test/resources/9999999/asset/v1/content/assets/query/post-response-assetType.idIN174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,14.json +7 -0
  396. package/test/resources/9999999/asset/v1/content/assets/query/post-response-assetType.idIN193,194,15.json +7 -0
  397. package/test/resources/9999999/asset/v1/content/assets/query/post-response-assetType.idIN195,196,197,198,199,200,201,202,203,210,211,212,213,3.json +46 -0
  398. package/test/resources/9999999/asset/v1/content/assets/query/post-response-assetType.idIN205,206,230,232,1.json +32 -0
  399. package/test/resources/9999999/asset/v1/content/assets/query/{post-response.json → post-response-assetType.idIN207,208,209,5.json} +45 -47
  400. package/test/resources/9999999/asset/v1/content/assets/query/post-response-assetType.idIN214,4.json +35 -0
  401. package/test/resources/9999999/asset/v1/content/assets/query/post-response-assetType.idIN215,216,217,218,219,220,221,222,223,224,225,226,227,228.json +7 -0
  402. package/test/resources/9999999/asset/v1/content/assets/query/post-response-customerKey=mcdev-issue-1157.json +46 -0
  403. package/test/resources/9999999/attributeGroup/retrieve-expected.json +1 -1
  404. package/test/resources/9999999/attributeSet/retrieve-expected.json +3 -3
  405. package/test/resources/9999999/automation/build-expected.json +7 -7
  406. package/test/resources/9999999/automation/create-expected.json +7 -7
  407. package/test/resources/9999999/automation/patch_fixKeys-pause-expected.json +6 -6
  408. package/test/resources/9999999/automation/patch_fixKeys-schedule-expected.json +6 -6
  409. package/test/resources/9999999/automation/retrieve-expected.json +7 -7
  410. package/test/resources/9999999/automation/template-expected.json +7 -7
  411. package/test/resources/9999999/automation/update-expected.json +6 -6
  412. package/test/resources/9999999/automation/v1/dataextracts/56c5370a-f988-4f36-b0ee-0f876573f6d7/delete-response.txt +1 -0
  413. package/test/resources/9999999/automation/v1/filetransfers/72c328ac-f5b0-4e37-91d3-a775666f15a6/delete-response.json +1 -0
  414. package/test/resources/9999999/automation/v1/imports/1ebf557b-372e-eb11-b81b-48df37d1dbd7/get-response.json +78 -0
  415. package/test/resources/9999999/automation/v1/imports/9d16f42c-2260-ed11-b849-48df37d1de8b/delete-response.txt +0 -0
  416. package/test/resources/9999999/automation/v1/imports/9d16f42c-2260-ed11-b849-48df37d1de8b/get-response.json +35 -0
  417. package/test/resources/9999999/automation/v1/imports/9d16f42c-2260-ed11-b849-48df37d1de8b/patch-response.json +14 -10
  418. package/test/resources/9999999/automation/v1/imports/get-response.json +78 -1
  419. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/get-response.json +1 -1
  420. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat_fixKeysSuffix/get-response.json +17 -0
  421. package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat_fixKeysSuffix/patch-response.json +18 -0
  422. package/test/resources/9999999/automation/v1/queries/abcde-607c-4940-afef-437965094dat/get-response.json +17 -0
  423. package/test/resources/9999999/automation/v1/queries/get-response.json +20 -3
  424. package/test/resources/9999999/automation/v1/scripts/39f6a488-20eb-4ba0-b0b9-023725b574e4/delete-response.txt +1 -0
  425. package/test/resources/9999999/automation/v1/scripts/get-response.json +3 -3
  426. package/test/resources/9999999/dataExtension/build-expected.json +3 -4
  427. package/test/resources/9999999/dataExtension/create-expected.json +1 -4
  428. package/test/resources/9999999/dataExtension/retrieve-Name=testExisting_dataExtension-response.xml +4 -4
  429. package/test/resources/9999999/dataExtension/retrieve-expected.json +3 -4
  430. package/test/resources/9999999/dataExtension/retrieve-response.xml +96 -1
  431. package/test/resources/9999999/dataExtension/template-expected.json +3 -4
  432. package/test/resources/9999999/dataExtension/update-expected.json +4 -3
  433. package/test/resources/9999999/dataExtension/update-response.xml +5 -3
  434. package/test/resources/9999999/dataExtensionField/retrieve-response.xml +72 -0
  435. package/test/resources/9999999/dataFolder/retrieve-ContentType=userinitiatedsends-response.xml +48 -0
  436. package/test/resources/9999999/deliveryProfile/get-expected.json +7 -0
  437. package/test/resources/9999999/emailSend/build-expected.json +43 -0
  438. package/test/resources/9999999/emailSend/get-expected.json +43 -0
  439. package/test/resources/9999999/emailSend/patch-expected.json +41 -0
  440. package/test/resources/9999999/emailSend/post-expected.json +41 -0
  441. package/test/resources/9999999/emailSend/template-expected.json +43 -0
  442. package/test/resources/9999999/emailSendDefinition/create-response.xml +98 -0
  443. package/test/resources/9999999/emailSendDefinition/delete-response.xml +36 -0
  444. package/test/resources/9999999/emailSendDefinition/retrieve-IsPlatformObject=falseANDDescriptionnotEqualsSFSendDefinition-response.xml +18 -8
  445. package/test/resources/9999999/emailSendDefinition/update-response.xml +97 -0
  446. package/test/resources/9999999/importDefinition/retrieve-CustomerKey=testExisting_importFile-response.xml +30 -0
  447. package/test/resources/9999999/importDefinition/retrieve-Name=testExisting_importFile-response.xml +30 -0
  448. package/test/resources/9999999/importFile/build-expected.json +11 -6
  449. package/test/resources/9999999/importFile/get-expected.json +13 -7
  450. package/test/resources/9999999/importFile/get-sms-expected.json +79 -0
  451. package/test/resources/9999999/importFile/patch-expected.json +12 -7
  452. package/test/resources/9999999/importFile/post-expected.json +8 -5
  453. package/test/resources/9999999/importFile/template-expected.json +10 -5
  454. package/test/resources/9999999/interaction/v1/eventDefinitions/get-response.json +124 -0
  455. package/test/resources/9999999/interaction/v1/interactions/3c3f4112-9b43-43ca-8a89-aa0375b2c1a2/delete-response.txt +1 -0
  456. package/test/resources/9999999/interaction/v1/interactions/3c3f4112-9b43-43ca-8a89-aa0375b2c1a2/get-response.json +253 -0
  457. package/test/resources/9999999/interaction/v1/interactions/get-response.json +83 -779
  458. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_Multistep/get-response.json +457 -0
  459. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_Quicksend/get-response.json +253 -0
  460. package/test/resources/9999999/interaction/v1/interactions/{key_0b76dccf-594c-b6dc-1acf-10c4493dcb84 → key_testExisting_temail}/get-response.json +5 -5
  461. package/test/resources/9999999/journey/build-expected.json +105 -449
  462. package/test/resources/9999999/journey/get-multistep-expected.json +413 -0
  463. package/test/resources/9999999/journey/get-quicksend-expected.json +232 -0
  464. package/test/resources/9999999/journey/get-quicksend-rcb-id-expected.json +232 -0
  465. package/test/resources/9999999/journey/get-quicksend-rcb-key-expected.json +232 -0
  466. package/test/resources/9999999/journey/get-quicksend-rcb-name-expected.json +232 -0
  467. package/test/resources/9999999/journey/get-transactionalEmail-expected.json +211 -0
  468. package/test/resources/9999999/journey/template-expected.json +105 -449
  469. package/test/resources/9999999/legacy/v1/beta/messaging/deliverypolicy/get-response.json +15 -0
  470. package/test/resources/9999999/legacy/v1/beta/mobile/imports/get-response.json +112 -0
  471. package/test/resources/9999999/legacy/v1/beta/mobile/message/NTQ3Ojc4OjA/get-response.json +1 -1
  472. package/test/resources/9999999/legacy/v1/beta/object/NWQwdnhEU3dFZWVBekJRQzdISWl0QTo0NTow/get-response.json +12 -0
  473. package/test/resources/9999999/list/retrieve-response.xml +13 -0
  474. package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json +1 -1
  475. package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json +1 -1
  476. package/test/resources/9999999/mobileMessage/build-expected.json +2 -5
  477. package/test/resources/9999999/mobileMessage/get-expected.json +2 -5
  478. package/test/resources/9999999/mobileMessage/post-create-expected.json +2 -4
  479. package/test/resources/9999999/mobileMessage/post-update-expected.json +2 -5
  480. package/test/resources/9999999/mobileMessage/template-expected.json +2 -5
  481. package/test/resources/9999999/query/build-expected.json +1 -1
  482. package/test/resources/9999999/query/build-expected.sql +2 -1
  483. package/test/resources/9999999/query/get-expected.json +1 -1
  484. package/test/resources/9999999/query/get-expected.sql +2 -1
  485. package/test/resources/9999999/query/get2-expected.json +1 -1
  486. package/test/resources/9999999/query/patch-expected.json +1 -1
  487. package/test/resources/9999999/query/patch_fixKeys-expected.json +1 -1
  488. package/test/resources/9999999/query/patch_fixKeysSuffix-expected.json +11 -0
  489. package/test/resources/9999999/query/patch_fixKeysSuffix-expected.sql +6 -0
  490. package/test/resources/9999999/query/post-expected.json +1 -1
  491. package/test/resources/9999999/query/template-expected.json +1 -1
  492. package/test/resources/9999999/query/template-expected.sql +2 -1
  493. package/test/resources/9999999/queryDefinition/retrieve-CustomerKey=testExisting_query2ANDStatus=Active-response.xml +30 -0
  494. package/test/resources/9999999/queryDefinition/retrieve-CustomerKey=testExisting_query_fixKeysSuffixANDStatus=Active-response.xml +30 -0
  495. package/test/resources/9999999/script/get_ampincluded-expected.html +5 -0
  496. package/test/resources/9999999/script/get_ampincluded-rcb-id-expected.html +5 -0
  497. package/test/resources/9999999/script/get_ampincluded-rcb-key-expected.html +5 -0
  498. package/test/resources/9999999/script/get_ampincluded-rcb-name-expected.html +5 -0
  499. package/test/resources/9999999/script/get_ampscript-expected.html +6 -1
  500. package/test/resources/9999999/script/get_ampscript-rcb-id-expected.html +8 -0
  501. package/test/resources/9999999/script/get_ampscript-rcb-key-expected.html +8 -0
  502. package/test/resources/9999999/script/get_ampscript-rcb-name-expected.html +10 -0
  503. package/test/resources/9999999/script/get_mixed-expected.html +5 -2
  504. package/test/resources/9999999/script/get_mixed-rcb-id-expected.html +12 -0
  505. package/test/resources/9999999/script/get_mixed-rcb-key-expected.html +12 -0
  506. package/test/resources/9999999/script/get_mixed-rcb-name-expected.html +14 -0
  507. package/test/resources/9999999/sendClassification/build-expected.json +8 -0
  508. package/test/resources/9999999/sendClassification/create-response.xml +51 -0
  509. package/test/resources/9999999/sendClassification/delete-response.xml +36 -0
  510. package/test/resources/9999999/sendClassification/get-expected.json +10 -0
  511. package/test/resources/9999999/sendClassification/patch-expected.json +8 -0
  512. package/test/resources/9999999/sendClassification/post-expected.json +8 -0
  513. package/test/resources/9999999/sendClassification/retrieve-response.xml +86 -0
  514. package/test/resources/9999999/sendClassification/template-expected.json +8 -0
  515. package/test/resources/9999999/sendClassification/update-response.xml +49 -0
  516. package/test/resources/9999999/senderProfile/build-expected.json +14 -0
  517. package/test/resources/9999999/senderProfile/create-response.xml +49 -0
  518. package/test/resources/9999999/senderProfile/delete-response.xml +36 -0
  519. package/test/resources/9999999/senderProfile/get-expected.json +16 -0
  520. package/test/resources/9999999/senderProfile/get-rcb-id-expected.json +16 -0
  521. package/test/resources/9999999/senderProfile/get-rcb-key-expected.json +16 -0
  522. package/test/resources/9999999/senderProfile/get-rcb-name-expected.json +16 -0
  523. package/test/resources/9999999/senderProfile/patch-expected.json +14 -0
  524. package/test/resources/9999999/senderProfile/post-expected.json +14 -0
  525. package/test/resources/9999999/senderProfile/retrieve-CustomerKey=Default-response.xml +44 -0
  526. package/test/resources/9999999/senderProfile/retrieve-CustomerKey=wrong-key-response.xml +26 -0
  527. package/test/resources/9999999/senderProfile/retrieve-response.xml +80 -0
  528. package/test/resources/9999999/senderProfile/template-expected.json +14 -0
  529. package/test/resources/9999999/senderProfile/update-response.xml +47 -0
  530. package/test/resources/9999999/transactionalEmail/build-expected.json +3 -3
  531. package/test/resources/9999999/transactionalEmail/get-expected.json +3 -3
  532. package/test/resources/9999999/transactionalEmail/patch-expected.json +3 -3
  533. package/test/resources/9999999/transactionalEmail/post-expected.json +2 -2
  534. package/test/resources/9999999/transactionalEmail/template-expected.json +3 -3
  535. package/test/resources/9999999/transactionalPush/build-expected.json +1 -1
  536. package/test/resources/9999999/transactionalPush/get-expected.json +1 -1
  537. package/test/resources/9999999/transactionalPush/patch-expected.json +1 -3
  538. package/test/resources/9999999/transactionalPush/post-expected.json +1 -3
  539. package/test/resources/9999999/transactionalPush/template-expected.json +1 -1
  540. package/test/resources/9999999/transactionalSMS/build-expected.amp +1 -1
  541. package/test/resources/9999999/transactionalSMS/build-expected.json +1 -2
  542. package/test/resources/9999999/transactionalSMS/get-expected.amp +1 -1
  543. package/test/resources/9999999/transactionalSMS/get-expected.json +1 -2
  544. package/test/resources/9999999/transactionalSMS/patch-expected.amp +1 -1
  545. package/test/resources/9999999/transactionalSMS/patch-expected.json +1 -2
  546. package/test/resources/9999999/transactionalSMS/post-expected.amp +1 -1
  547. package/test/resources/9999999/transactionalSMS/post-expected.json +1 -2
  548. package/test/resources/9999999/transactionalSMS/template-expected.amp +1 -1
  549. package/test/resources/9999999/transactionalSMS/template-expected.json +1 -2
  550. package/test/resources/9999999/triggeredSend/build-expected.json +5 -6
  551. package/test/resources/9999999/triggeredSend/get-expected.json +5 -6
  552. package/test/resources/9999999/triggeredSend/get-rcb-id-expected.json +29 -0
  553. package/test/resources/9999999/triggeredSend/get-rcb-key-expected.json +29 -0
  554. package/test/resources/9999999/triggeredSend/get-rcb-name-expected.json +29 -0
  555. package/test/resources/9999999/triggeredSend/patch-expected.json +5 -6
  556. package/test/resources/9999999/triggeredSend/post-expected.json +5 -6
  557. package/test/resources/9999999/triggeredSend/template-expected.json +5 -6
  558. package/test/resources/9999999/triggeredSendDefinition/create-response.xml +6 -1
  559. package/test/resources/9999999/triggeredSendDefinition/retrieve-TriggeredSendStatusINNew,Active,Inactive,Moved,Canceled-response.xml +53 -1
  560. package/test/resources/9999999/triggeredSendDefinition/update-response.xml +6 -1
  561. package/test/resources/9999999/verification/build-expected.json +1 -1
  562. package/test/resources/9999999/verification/get-expected.json +1 -1
  563. package/test/resources/9999999/verification/patch-expected.json +1 -1
  564. package/test/resources/9999999/verification/post-expected.json +1 -1
  565. package/test/resources/9999999/verification/template-expected.json +1 -1
  566. package/test/type.asset.test.js +733 -17
  567. package/test/type.attributeGroup.test.js +6 -2
  568. package/test/type.attributeSet.test.js +6 -2
  569. package/test/type.automation.test.js +174 -162
  570. package/test/type.dataExtension.test.js +41 -45
  571. package/test/type.dataExtract.test.js +22 -29
  572. package/test/type.deliveryProfile.test.js +45 -0
  573. package/test/type.emailSend.test.js +144 -0
  574. package/test/type.event.test.js +62 -0
  575. package/test/type.fileTransfer.test.js +21 -29
  576. package/test/type.importFile.test.js +61 -35
  577. package/test/type.journey.test.js +251 -45
  578. package/test/type.mobileKeyword.test.js +46 -54
  579. package/test/type.mobileMessage.test.js +28 -27
  580. package/test/type.query.test.js +303 -124
  581. package/test/type.script.test.js +288 -69
  582. package/test/type.sendClassification.test.js +156 -0
  583. package/test/type.senderProfile.test.js +254 -0
  584. package/test/type.transactionalEmail.test.js +15 -11
  585. package/test/type.transactionalPush.test.js +15 -11
  586. package/test/type.transactionalSMS.test.js +30 -23
  587. package/test/type.triggeredSend.test.js +130 -17
  588. package/test/type.user.test.js +22 -14
  589. package/test/type.verification.test.js +13 -10
  590. package/test/utils.js +76 -21
  591. package/tsconfig.json +21 -0
  592. package/types/mcdev.d.js +190 -66
  593. package/docs/dist/documentation.md +0 -8878
  594. package/test/mockRoot/deploy/testInstance/testBU/journey/testExisting_interaction.interaction-meta.json +0 -576
  595. package/test/mockRoot/deploy/testInstance/testBU/journey/testNew_interaction.interaction-meta.json +0 -266
  596. package/test/resources/9999999/interaction/v1/EventDefinitions/get-response.json +0 -43
  597. package/test/resources/9999999/interaction/v1/interactions/233d4413-922c-4568-85a5-e5cc77efc3be/delete-response.json +0 -1
  598. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/get-response.json +0 -592
  599. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/put-response.json +0 -592
  600. package/test/resources/9999999/interaction/v1/interactions/post-response.json +0 -280
  601. package/test/resources/9999999/journey/get-expected.json +0 -576
  602. package/test/resources/9999999/script/get_ampincluded-expected.ssjs +0 -5
@@ -1,13 +1,31 @@
1
1
  'use strict';
2
2
 
3
3
  import MetadataType from './MetadataType.js';
4
- import TYPE from '../../types/mcdev.d.js';
5
4
  import { Util } from '../util/util.js';
6
5
  import File from '../util/file.js';
7
6
  import pLimit from 'p-limit';
8
7
  import cliProgress from 'cli-progress';
9
8
  import cache from '../util/cache.js';
10
9
  import TriggeredSend from './TriggeredSend.js';
10
+ import Folder from './Folder.js';
11
+ import ReplaceCbReference from '../util/replaceContentBlockReference.js';
12
+
13
+ /**
14
+ * @typedef {import('../../types/mcdev.d.js').BuObject} BuObject
15
+ * @typedef {import('../../types/mcdev.d.js').CodeExtract} CodeExtract
16
+ * @typedef {import('../../types/mcdev.d.js').CodeExtractItem} CodeExtractItem
17
+ * @typedef {import('../../types/mcdev.d.js').MetadataTypeItem} MetadataTypeItem
18
+ * @typedef {import('../../types/mcdev.d.js').MetadataTypeItemDiff} MetadataTypeItemDiff
19
+ * @typedef {import('../../types/mcdev.d.js').MetadataTypeMap} MetadataTypeMap
20
+ * @typedef {import('../../types/mcdev.d.js').SoapRequestParams} SoapRequestParams
21
+ * @typedef {import('../../types/mcdev.d.js').TemplateMap} TemplateMap
22
+ */
23
+ /**
24
+ * @typedef {import('../../types/mcdev.d.js').AssetSubType} AssetSubType
25
+ * @typedef {import('../../types/mcdev.d.js').AssetMap} AssetMap
26
+ * @typedef {import('../../types/mcdev.d.js').AssetItem} AssetItem
27
+ * @typedef {import('../../types/mcdev.d.js').AssetRequestParams} AssetRequestParams
28
+ */
11
29
 
12
30
  /**
13
31
  * FileTransfer MetadataType
@@ -19,13 +37,23 @@ class Asset extends MetadataType {
19
37
  * Retrieves Metadata of Asset
20
38
  *
21
39
  * @param {string} retrieveDir Directory where retrieved metadata directory will be saved
22
- * @param {void} _ -
23
- * @param {TYPE.AssetSubType[]} [subTypeArr] optionally limit to a single subtype
40
+ * @param {void | string[]} _ unused parameter
41
+ * @param {string[]} [subTypeArr] optionally limit to a single AssetSubType
24
42
  * @param {string} [key] customer key
25
- * @returns {Promise.<{metadata: TYPE.AssetMap, type: string}>} Promise
43
+ * @param {boolean} [loadShared] optionally retrieve assets from other BUs that were shared with the current
44
+ * @returns {Promise.<{metadata: AssetMap, type: string}>} Promise
26
45
  */
27
- static async retrieve(retrieveDir, _, subTypeArr, key) {
46
+ static async retrieve(retrieveDir, _, subTypeArr, key, loadShared = false) {
28
47
  const items = [];
48
+ if (subTypeArr) {
49
+ // check if elements in subTypeArr exist in this.definition.subTypes
50
+ const invalidSubTypes = subTypeArr.filter(
51
+ (subType) => !this.definition.subTypes.includes(subType)
52
+ );
53
+ if (invalidSubTypes.length) {
54
+ throw new Error(`Invalid subType(s) found: ${invalidSubTypes.join(', ')}`);
55
+ }
56
+ }
29
57
  subTypeArr ||= this._getSubTypes();
30
58
  if (retrieveDir) {
31
59
  await File.initPrettier();
@@ -34,7 +62,7 @@ class Asset extends MetadataType {
34
62
  for (const subType of subTypeArr) {
35
63
  // each subtype contains multiple different specific types (images contains jpg and png for example)
36
64
  // we use await here to limit the risk of too many concurrent api requests at time
37
- items.push(...(await this.requestSubType(subType, retrieveDir, null, null, key)));
65
+ items.push(...(await this.requestSubType(subType, retrieveDir, key, null, loadShared)));
38
66
  }
39
67
  const metadata = this.parseResponseBody({ items: items });
40
68
  if (retrieveDir) {
@@ -45,16 +73,55 @@ class Asset extends MetadataType {
45
73
  }
46
74
  return { metadata: metadata, type: this.definition.type };
47
75
  }
76
+ /**
77
+ * Helper for writing Metadata to disk, used for Retrieve and deploy
78
+ *
79
+ * @param {MetadataTypeMap} results metadata results from deploy
80
+ * @param {string} retrieveDir directory where metadata should be stored after deploy/retrieve
81
+ * @param {string} [overrideType] for use when there is a subtype (such as folder-queries)
82
+ * @param {TemplateMap} [templateVariables] variables to be replaced in the metadata
83
+ * @returns {Promise.<MetadataTypeMap>} Promise of saved metadata
84
+ */
85
+ static async saveResults(results, retrieveDir, overrideType, templateVariables) {
86
+ if (Object.keys(results).length) {
87
+ // only execute the following if records were found
88
+ await this._postRetrieveTasksBulk(results);
89
+ }
90
+ return super.saveResults(results, retrieveDir, overrideType, templateVariables);
91
+ }
92
+ /**
93
+ * helper for Journey's {@link Asset.saveResults}. Gets executed after retreive of metadata type and
94
+ *
95
+ * @param {MetadataTypeMap} metadataMap key=customer key, value=metadata
96
+ */
97
+ static async _postRetrieveTasksBulk(metadataMap) {
98
+ // Template-Based Email
99
+ let needTemplates = false;
100
+ for (const key in metadataMap) {
101
+ if (metadataMap[key].assetType.name == 'templatebasedemail') {
102
+ needTemplates = true;
103
+ break;
104
+ }
105
+ }
106
+
107
+ if (needTemplates && !cache.getCache()?.asset) {
108
+ // for
109
+ Util.logger.info(' - Caching dependent Metadata: asset-template');
110
+ const result = await this.retrieveForCache(undefined, ['template']);
111
+ cache.setMetadata('asset', result.metadata);
112
+ }
113
+ }
48
114
 
49
115
  /**
50
116
  * Retrieves asset metadata for caching
51
117
  *
52
- * @param {void} _ unused
118
+ * @param {void | string[]} [_] parameter not used
53
119
  * @param {string[]} [subTypeArr] optionally limit to a single subtype
54
- * @returns {Promise.<{metadata: TYPE.AssetMap, type: string}>} Promise
120
+ * @param {boolean} [loadShared] optionally retrieve assets from other BUs that were shared with the current
121
+ * @returns {Promise.<{metadata: AssetMap, type: string}>} Promise
55
122
  */
56
- static retrieveForCache(_, subTypeArr) {
57
- return this.retrieve(null, null, subTypeArr);
123
+ static retrieveForCache(_, subTypeArr, loadShared = false) {
124
+ return this.retrieve(null, null, subTypeArr, undefined, loadShared);
58
125
  }
59
126
 
60
127
  /**
@@ -62,9 +129,9 @@ class Asset extends MetadataType {
62
129
  *
63
130
  * @param {string} templateDir Directory where retrieved metadata directory will be saved
64
131
  * @param {string} name name of the metadata file
65
- * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
66
- * @param {TYPE.AssetSubType} [selectedSubType] optionally limit to a single subtype
67
- * @returns {Promise.<{metadata: TYPE.AssetItem, type: string}>} Promise
132
+ * @param {TemplateMap} templateVariables variables to be replaced in the metadata
133
+ * @param {AssetSubType} [selectedSubType] optionally limit to a single subtype
134
+ * @returns {Promise.<{metadata: AssetItem, type: string}>} Promise
68
135
  */
69
136
  static async retrieveAsTemplate(templateDir, name, templateVariables, selectedSubType) {
70
137
  const items = [];
@@ -75,7 +142,12 @@ class Asset extends MetadataType {
75
142
  // each subtype contains multiple different specific types (images contains jpg and png for example)
76
143
  // we use await here to limit the risk of too many concurrent api requests at time
77
144
  items.push(
78
- ...(await this.requestSubType(subType, templateDir, name, templateVariables))
145
+ ...(await this.requestSubType(
146
+ subType,
147
+ templateDir,
148
+ 'name:' + name,
149
+ templateVariables
150
+ ))
79
151
  );
80
152
  }
81
153
  const metadata = this.parseResponseBody({ items: items });
@@ -90,7 +162,7 @@ class Asset extends MetadataType {
90
162
  * helper for {@link Asset.retrieve} + {@link Asset.retrieveAsTemplate}
91
163
  *
92
164
  * @private
93
- * @returns {TYPE.AssetSubType[]} subtype array
165
+ * @returns {string[]} AssetSubType array
94
166
  */
95
167
  static _getSubTypes() {
96
168
  const selectedSubTypeArr = this.properties.metaDataTypes.retrieve.filter((type) =>
@@ -112,7 +184,7 @@ class Asset extends MetadataType {
112
184
  /**
113
185
  * Creates a single asset
114
186
  *
115
- * @param {TYPE.AssetItem} metadata a single asset
187
+ * @param {AssetItem} metadata a single asset
116
188
  * @returns {Promise} Promise
117
189
  */
118
190
  static create(metadata) {
@@ -124,7 +196,7 @@ class Asset extends MetadataType {
124
196
  /**
125
197
  * Updates a single asset
126
198
  *
127
- * @param {TYPE.AssetItem} metadata a single asset
199
+ * @param {AssetItem} metadata a single asset
128
200
  * @returns {Promise} Promise
129
201
  */
130
202
  static update(metadata) {
@@ -134,25 +206,26 @@ class Asset extends MetadataType {
134
206
  /**
135
207
  * Retrieves Metadata of a specific asset type
136
208
  *
137
- * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
209
+ * @param {string} subType group of similar assets to put in a folder (ie. images)
138
210
  * @param {string} [retrieveDir] target directory for saving assets
139
- * @param {string} [templateName] name of the metadata file
140
- * @param {TYPE.TemplateMap} [templateVariables] variables to be replaced in the metadata
141
- * @param {string} key customer key to filter by
142
- * @returns {Promise} Promise
211
+ * @param {string} [key] key/id/name to filter by
212
+ * @param {TemplateMap} [templateVariables] variables to be replaced in the metadata
213
+ * @param {boolean} [loadShared] optionally retrieve assets from other BUs that were shared with the current
214
+ * @returns {Promise.<object[]>} Promise
143
215
  */
144
- static async requestSubType(subType, retrieveDir, templateName, templateVariables, key) {
216
+ static async requestSubType(subType, retrieveDir, key, templateVariables, loadShared = false) {
145
217
  if (retrieveDir) {
146
218
  Util.logger.info(`- Retrieving Subtype: ${subType}`);
147
219
  } else {
148
220
  Util.logger.info(` - Caching Subtype: ${subType}`);
149
221
  }
150
- /** @type {TYPE.AssetSubType[]} */
222
+ /** @type {AssetSubType[]} */
151
223
  const extendedSubTypeArr = this.definition.extendedSubTypes[subType];
152
224
  const subtypeIds = extendedSubTypeArr?.map(
153
225
  (subTypeItemName) => Asset.definition.typeMapping[subTypeItemName]
154
226
  );
155
- const uri = '/asset/v1/content/assets/query';
227
+ const uri = '/asset/v1/content/assets/query' + (loadShared ? '?scope=shared' : '');
228
+ /** @type {AssetRequestParams} */
156
229
  const payload = {
157
230
  page: {
158
231
  page: 1,
@@ -162,7 +235,7 @@ class Asset extends MetadataType {
162
235
  fields: ['category', 'createdDate', 'createdBy', 'modifiedDate', 'modifiedBy'], // get folder to allow duplicate name check against cache
163
236
  };
164
237
 
165
- if (templateName) {
238
+ if (key) {
166
239
  payload.query = {
167
240
  leftOperand: {
168
241
  property: 'assetType.id',
@@ -170,26 +243,26 @@ class Asset extends MetadataType {
170
243
  value: subtypeIds,
171
244
  },
172
245
  logicalOperator: 'AND',
173
- rightOperand: {
246
+ };
247
+ if (key.startsWith('id:')) {
248
+ payload.query.rightOperand = {
249
+ property: this.definition.idField,
250
+ simpleOperator: 'equal',
251
+ value: key.slice(3),
252
+ };
253
+ } else if (key.startsWith('name:')) {
254
+ payload.query.rightOperand = {
174
255
  property: this.definition.nameField,
175
256
  simpleOperator: 'equal',
176
- value: templateName,
177
- },
178
- };
179
- } else if (key) {
180
- payload.query = {
181
- leftOperand: {
182
- property: 'assetType.id',
183
- simpleOperator: 'in',
184
- value: subtypeIds,
185
- },
186
- logicalOperator: 'AND',
187
- rightOperand: {
257
+ value: key.slice(5),
258
+ };
259
+ } else {
260
+ payload.query.rightOperand = {
188
261
  property: this.definition.keyField,
189
262
  simpleOperator: 'equal',
190
263
  value: key,
191
- },
192
- };
264
+ };
265
+ }
193
266
  } else {
194
267
  payload.query = {
195
268
  property: 'assetType.id',
@@ -252,20 +325,21 @@ class Asset extends MetadataType {
252
325
  } while (moreResults);
253
326
 
254
327
  // only when we save results do we need the complete metadata or files. caching can skip these
255
- if (retrieveDir && items.length > 0) {
256
- for (const item of items) {
257
- if (item.customerKey.trim() !== item.customerKey) {
258
- Util.logger.warn(
259
- ` - ${this.definition.type} ${item[this.definition.nameField]} (${
260
- item[this.definition.keyField]
261
- }) has leading or trailing spaces in customerKey. Please remove them in SFMC.`
262
- );
328
+ if (retrieveDir) {
329
+ if (items.length > 0) {
330
+ for (const item of items) {
331
+ if (item.customerKey.trim() !== item.customerKey) {
332
+ Util.logger.warn(
333
+ ` - ${this.definition.type} ${item[this.definition.nameField]} (${
334
+ item[this.definition.keyField]
335
+ }) has leading or trailing spaces in customerKey. Please remove them in SFMC.`
336
+ );
337
+ }
263
338
  }
339
+ // we have to wait on execution or it potentially causes memory reference issues when changing between BUs
340
+ await this.requestAndSaveExtended(items, subType, retrieveDir, templateVariables);
264
341
  }
265
- // we have to wait on execution or it potentially causes memory reference issues when changing between BUs
266
- await this.requestAndSaveExtended(items, subType, retrieveDir, templateVariables);
267
- Util.logger.debug(`Downloaded asset-${subType}: ${items.length}`);
268
- } else if (retrieveDir && !items.length) {
342
+ // always show the summary even if we already had the progress bar in the console
269
343
  Util.logger.info(` Downloaded asset-${subType}: ${items.length}`);
270
344
  }
271
345
 
@@ -278,10 +352,10 @@ class Asset extends MetadataType {
278
352
  * Retrieves extended metadata (files or extended content) of asset
279
353
  *
280
354
  * @param {Array} items array of items to retrieve
281
- * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
355
+ * @param {string} subType group of similar assets to put in a folder (ie. images)
282
356
  * @param {string} retrieveDir target directory for saving assets
283
- * @param {TYPE.TemplateMap} [templateVariables] variables to be replaced in the metadata
284
- * @returns {Promise} Promise
357
+ * @param {TemplateMap} [templateVariables] variables to be replaced in the metadata
358
+ * @returns {Promise.<MetadataTypeMap>} Promise
285
359
  */
286
360
  static async requestAndSaveExtended(items, subType, retrieveDir, templateVariables) {
287
361
  // disable CLI logs other than error while retrieving subtype
@@ -293,7 +367,7 @@ class Asset extends MetadataType {
293
367
  const extendedBar = new cliProgress.SingleBar(
294
368
  {
295
369
  format:
296
- ' Downloaded [{bar}] {percentage}% | {value}/{total} | asset-' +
370
+ ' Downloading [{bar}] {percentage}% | {value}/{total} | asset-' +
297
371
  subType,
298
372
  },
299
373
  cliProgress.Presets.shades_classic
@@ -301,6 +375,8 @@ class Asset extends MetadataType {
301
375
 
302
376
  const completed = [];
303
377
  const failed = [];
378
+ const metadataMap = {};
379
+
304
380
  // put in do loop to manage issues with connection timeout
305
381
  do {
306
382
  // use promise execution limiting to avoid rate limits on api, but speed up execution
@@ -310,9 +386,8 @@ class Asset extends MetadataType {
310
386
  const rateLimit = pLimit(5);
311
387
 
312
388
  const promiseMap = await Promise.all(
313
- items.map((item) =>
389
+ items.map((item, index) =>
314
390
  rateLimit(async () => {
315
- const metadata = {};
316
391
  // this is a file so extended is at another endpoint
317
392
  if (item?.fileProperties?.extension && !completed.includes(item.id)) {
318
393
  try {
@@ -322,7 +397,7 @@ class Asset extends MetadataType {
322
397
  failed.push({ item: item, error: ex });
323
398
  }
324
399
  // even if the extended file failed, still save the metadata
325
- metadata[item.customerKey] = item;
400
+ metadataMap[item.customerKey] = item;
326
401
  }
327
402
  // this is a complex type which stores data in the asset itself
328
403
  else if (!completed.includes(item.id)) {
@@ -331,15 +406,17 @@ class Asset extends MetadataType {
331
406
  'asset/v1/content/assets/' + item.id
332
407
  );
333
408
  // only save the metadata if we have extended content
334
- metadata[item.customerKey] = extendedItem;
409
+ metadataMap[item.customerKey] = extendedItem;
410
+ // overwrite the original item with the extended content to ensure retrieve() returns it
411
+ items[index] = extendedItem;
335
412
  } catch (ex) {
336
413
  failed.push({ item: item, error: ex });
337
414
  }
338
415
  }
339
416
  completed.push(item.id);
340
- if (metadata[item.customerKey]) {
417
+ if (metadataMap[item.customerKey]) {
341
418
  await this.saveResults(
342
- metadata,
419
+ metadataMap,
343
420
  retrieveDir,
344
421
  'asset-' + subType,
345
422
  templateVariables
@@ -364,6 +441,7 @@ class Asset extends MetadataType {
364
441
  }
365
442
  }
366
443
  } while (completed.length === items.length);
444
+ return metadataMap;
367
445
  }
368
446
 
369
447
  /**
@@ -375,28 +453,26 @@ class Asset extends MetadataType {
375
453
  */
376
454
  static _resetLogLevel(loggerLevelBak, failed) {
377
455
  // re-enable CLI logs
378
- if (loggerLevelBak !== 'error') {
379
- // reset logging level
380
- let obj;
381
- switch (loggerLevelBak) {
382
- case 'info': {
383
- obj = {};
384
- break;
385
- }
386
- case 'verbose': {
387
- obj = { verbose: true };
388
- break;
389
- }
390
- case 'debug': {
391
- obj = { debug: true };
392
- break;
393
- }
394
- case 'error': {
395
- obj = { silent: true };
396
- }
456
+ // reset logging level
457
+ let obj;
458
+ switch (loggerLevelBak) {
459
+ case 'info': {
460
+ obj = {};
461
+ break;
462
+ }
463
+ case 'verbose': {
464
+ obj = { verbose: true };
465
+ break;
466
+ }
467
+ case 'debug': {
468
+ obj = { debug: true };
469
+ break;
470
+ }
471
+ case 'error': {
472
+ obj = { silent: true };
397
473
  }
398
- Util.setLoggingLevel(obj);
399
474
  }
475
+ Util.setLoggingLevel(obj);
400
476
 
401
477
  if (failed.length) {
402
478
  Util.logger.warn(
@@ -428,8 +504,8 @@ class Asset extends MetadataType {
428
504
  * Some metadata types store their actual content as a separate file, e.g. images
429
505
  * This method retrieves these and saves them alongside the metadata json
430
506
  *
431
- * @param {TYPE.AssetItem} metadata a single asset
432
- * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
507
+ * @param {AssetItem} metadata a single asset
508
+ * @param {string} subType group of similar assets to put in a folder (ie. images)
433
509
  * @param {string} retrieveDir target directory for saving assets
434
510
  * @returns {Promise.<void>} -
435
511
  */
@@ -453,8 +529,8 @@ class Asset extends MetadataType {
453
529
  * Some metadata types store their actual content as a separate file, e.g. images
454
530
  * This method reads these from the local FS stores them in the metadata object allowing to deploy it
455
531
  *
456
- * @param {TYPE.AssetItem} metadata a single asset
457
- * @param {TYPE.AssetSubType} subType group of similar assets to put in a folder (ie. images)
532
+ * @param {AssetItem} metadata a single asset
533
+ * @param {string} subType group of similar assets to put in a folder (ie. images)
458
534
  * @param {string} deployDir directory of deploy files
459
535
  * @param {boolean} [pathOnly] used by getFilesToCommit which does not need the binary file to be actually read
460
536
  * @returns {Promise.<string>} if found will return the path of the binary file
@@ -488,22 +564,49 @@ class Asset extends MetadataType {
488
564
  /**
489
565
  * manages post retrieve steps
490
566
  *
491
- * @param {TYPE.AssetItem} metadata a single asset
492
- * @returns {TYPE.CodeExtractItem} metadata
567
+ * @param {AssetItem} metadata a single asset
568
+ * @returns {CodeExtractItem} metadata
493
569
  */
494
570
  static postRetrieveTasks(metadata) {
495
571
  // folder
496
572
  this.setFolderPath(metadata);
573
+
574
+ // template-based emails
575
+ if (metadata.assetType.name === 'templatebasedemail') {
576
+ // get template
577
+ try {
578
+ if (metadata.views?.html?.template?.id) {
579
+ metadata.views.html.template.r__asset_key = cache.searchForField(
580
+ 'asset',
581
+ metadata.views?.html?.template?.id,
582
+ 'id',
583
+ 'customerKey'
584
+ );
585
+ delete metadata.views.html.template.id;
586
+ delete metadata.views.html.template.name;
587
+ delete metadata.views.html.template.assetType;
588
+ delete metadata.views.html.template.availableViews;
589
+ delete metadata.views.html.template.data;
590
+ delete metadata.views.html.template.modelVersion;
591
+ }
592
+ } catch {
593
+ Util.logger.warn(
594
+ ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
595
+ metadata[this.definition.keyField]
596
+ }): Could not find email template with id (${metadata.views.html.template.id})`
597
+ );
598
+ }
599
+ }
600
+
497
601
  // extract HTML for selected subtypes and convert payload for easier processing in MetadataType.saveResults()
498
- metadata = this._extractCode(metadata);
499
- return metadata;
602
+ return this._extractCode(metadata);
500
603
  }
501
604
 
502
605
  /**
503
606
  * Gets executed after deployment of metadata type
504
607
  *
505
- * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField
506
- * @param {TYPE.MetadataTypeMap} _ originalMetadata to be updated (contains additioanl fields)
608
+ * @param {MetadataTypeMap} metadata metadata mapped by their keyField
609
+ * @param {MetadataTypeMap} _ originalMetadata to be updated (contains additioanl fields)
507
610
  * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates
508
611
  * @returns {Promise.<void>} -
509
612
  */
@@ -524,7 +627,7 @@ class Asset extends MetadataType {
524
627
  * helper for {@link Asset.postDeployTasks}. triggers a refresh of active triggerredSendDefinitions associated with the updated asset-message items. Gets executed if refresh option has been set.
525
628
  *
526
629
  * @private
527
- * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField
630
+ * @param {MetadataTypeMap} metadata metadata mapped by their keyField
528
631
  * @returns {Promise.<void>} -
529
632
  */
530
633
  static async _refreshTriggeredSend(metadata) {
@@ -572,9 +675,9 @@ class Asset extends MetadataType {
572
675
  /**
573
676
  * prepares an asset definition for deployment
574
677
  *
575
- * @param {TYPE.AssetItem} metadata a single asset
678
+ * @param {AssetItem} metadata a single asset
576
679
  * @param {string} deployDir directory of deploy files
577
- * @returns {Promise.<TYPE.AssetItem>} Promise
680
+ * @returns {Promise.<AssetItem>} Promise
578
681
  */
579
682
  static async preDeployTasks(metadata, deployDir) {
580
683
  // additonalattributes fail where the value is "" so we need to remove them from deploy
@@ -587,6 +690,39 @@ class Asset extends MetadataType {
587
690
  // folder
588
691
  this.setFolderId(metadata);
589
692
 
693
+ // template-based emails
694
+ if (
695
+ metadata.assetType.name === 'templatebasedemail' &&
696
+ metadata.views?.html?.template?.r__asset_key
697
+ ) {
698
+ // template
699
+ metadata.views.html.template.id = cache.searchForField(
700
+ 'asset',
701
+ metadata.views.html.template.r__asset_key,
702
+ 'customerKey',
703
+ 'id'
704
+ );
705
+ metadata.views.html.template.name = cache.searchForField(
706
+ 'asset',
707
+ metadata.views.html.template.r__asset_key,
708
+ 'customerKey',
709
+ 'name'
710
+ );
711
+ metadata.views.html.template.assetType = {
712
+ id: 4,
713
+ name: 'template',
714
+ displayName: 'Template',
715
+ };
716
+
717
+ metadata.views.html.template.data = {
718
+ email: { options: { generateFrom: null } },
719
+ };
720
+
721
+ metadata.views.html.template.availableViews = [];
722
+ metadata.views.html.template.modelVersion = 2;
723
+ delete metadata.views.html.template.r__asset_key;
724
+ }
725
+
590
726
  // restore asset type id which is needed for deploy
591
727
  metadata.assetType.id = this.definition.typeMapping[metadata.assetType.name];
592
728
 
@@ -601,8 +737,9 @@ class Asset extends MetadataType {
601
737
 
602
738
  // only execute #3 if we are deploying / copying from one BU to another, not while using mcdev as a developer tool
603
739
  if (
740
+ !Util.OPTIONS.noMidSuffix &&
604
741
  this.buObject.mid &&
605
- metadata.memberId !== this.buObject.mid &&
742
+ metadata.memberId != this.buObject.mid && // soft comparison to accomodate for string-version of mid
606
743
  !metadata[this.definition.keyField].endsWith(this.buObject.mid)
607
744
  ) {
608
745
  // #3 make sure customer key is unique by suffixing it with target MID (unless we are deploying to the same MID)
@@ -618,7 +755,7 @@ class Asset extends MetadataType {
618
755
  const namesInFolder = Object.keys(assetCache)
619
756
  .filter((key) => assetCache[key].category.id === metadata.category.id)
620
757
  .map((key) => ({
621
- type: this._getMainSubtype(assetCache[key].assetType.name),
758
+ type: this.#getMainSubtype(assetCache[key].assetType.name),
622
759
  key: key,
623
760
  name: assetCache[key].name,
624
761
  }));
@@ -626,7 +763,7 @@ class Asset extends MetadataType {
626
763
  metadata[this.definition.nameField] = this._findUniqueName(
627
764
  metadata[this.definition.keyField],
628
765
  metadata[this.definition.nameField],
629
- this._getMainSubtype(metadata.assetType.name),
766
+ this.#getMainSubtype(metadata.assetType.name),
630
767
  namesInFolder
631
768
  );
632
769
  return metadata;
@@ -637,7 +774,7 @@ class Asset extends MetadataType {
637
774
  * @param {string} extendedSubType webpage, htmlblock, etc
638
775
  * @returns {string} subType: block, message, other, etc
639
776
  */
640
- static _getMainSubtype(extendedSubType) {
777
+ static #getMainSubtype(extendedSubType) {
641
778
  return Object.keys(this.definition.extendedSubTypes).find((subType) =>
642
779
  this.definition.extendedSubTypes[subType].includes(extendedSubType)
643
780
  );
@@ -649,7 +786,7 @@ class Asset extends MetadataType {
649
786
  * @param {string} key key of the asset
650
787
  * @param {string} name name of the asset
651
788
  * @param {string} type assetType-name
652
- * @param {string[]} namesInFolder names of the assets in the same folder
789
+ * @param {{ type: string; key: string; name: any; }[]} namesInFolder names of the assets in the same folder
653
790
  * @returns {string} new name
654
791
  */
655
792
  static _findUniqueName(key, name, type, namesInFolder) {
@@ -672,8 +809,8 @@ class Asset extends MetadataType {
672
809
  * determines the subtype of the current asset
673
810
  *
674
811
  * @private
675
- * @param {TYPE.AssetItem} metadata a single asset
676
- * @returns {TYPE.AssetSubType | void} subtype
812
+ * @param {AssetItem} metadata a single asset
813
+ * @returns {string} subtype
677
814
  */
678
815
  static _getSubtype(metadata) {
679
816
  for (const sub in this.definition.extendedSubTypes) {
@@ -681,6 +818,7 @@ class Asset extends MetadataType {
681
818
  return sub;
682
819
  }
683
820
  }
821
+ return;
684
822
  }
685
823
  /**
686
824
  * helper for {@link MetadataType.buildDefinition}
@@ -688,10 +826,10 @@ class Asset extends MetadataType {
688
826
  *
689
827
  * @param {string} templateDir Directory where metadata templates are stored
690
828
  * @param {string} targetDir Directory where built definitions will be saved
691
- * @param {TYPE.AssetItem} metadata main JSON file that was read from file system
692
- * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
829
+ * @param {AssetItem} metadata main JSON file that was read from file system
830
+ * @param {TemplateMap} templateVariables variables to be replaced in the metadata
693
831
  * @param {string} templateName name of the template to be built
694
- * @returns {Promise.<void>} -
832
+ * @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
695
833
  */
696
834
  static buildDefinitionForNested(
697
835
  templateDir,
@@ -716,10 +854,10 @@ class Asset extends MetadataType {
716
854
  * @example assets of type codesnippetblock will result in 1 json and 1 amp/html file. both files need to be run through templating
717
855
  * @param {string} templateDir Directory where metadata templates are stored
718
856
  * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
719
- * @param {TYPE.AssetItem} metadata main JSON file that was read from file system
720
- * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
857
+ * @param {AssetItem} metadata main JSON file that was read from file system
858
+ * @param {TemplateMap} templateVariables variables to be replaced in the metadata
721
859
  * @param {string} templateName name of the template to be built
722
- * @returns {Promise.<void>} -
860
+ * @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
723
861
  */
724
862
  static buildTemplateForNested(
725
863
  templateDir,
@@ -743,12 +881,12 @@ class Asset extends MetadataType {
743
881
  * handles extracted code if any are found for complex types
744
882
  *
745
883
  * @param {string} templateDir Directory where metadata templates are stored
746
- * @param {string} targetDir Directory where built definitions will be saved
747
- * @param {TYPE.AssetItem} metadata main JSON file that was read from file system
748
- * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
884
+ * @param {string|string[]} targetDir (List of) Directory where built definitions will be saved
885
+ * @param {AssetItem} metadata main JSON file that was read from file system
886
+ * @param {TemplateMap} templateVariables variables to be replaced in the metadata
749
887
  * @param {string} templateName name of the template to be built
750
888
  * @param {'definition'|'template'} mode defines what we use this helper for
751
- * @returns {Promise.<void>} -
889
+ * @returns {Promise.<string[][]>} list of extracted files with path-parts provided as an array
752
890
  */
753
891
  static async _buildForNested(
754
892
  templateDir,
@@ -759,7 +897,7 @@ class Asset extends MetadataType {
759
897
  mode
760
898
  ) {
761
899
  // * because asset's _mergeCode() is overwriting 'metadata', clone it to ensure the main file is not modified by what we do in here
762
- metadata = JSON.parse(JSON.stringify(metadata));
900
+ metadata = structuredClone(metadata);
763
901
 
764
902
  // #1 text extracts
765
903
  // define asset's subtype
@@ -789,6 +927,9 @@ class Asset extends MetadataType {
789
927
  }: ${extractedFile.fileName}.${extractedFile.fileExt}.`
790
928
  );
791
929
  }
930
+ extractedFile.subFolder = extractedFile.subFolder
931
+ .map((el) => (el === templateName ? metadata[this.definition.keyField] : el))
932
+ .map((el) => this.applyTemplateValues(el, templateVariables));
792
933
  }
793
934
 
794
935
  // #2 binary extracts
@@ -816,6 +957,7 @@ class Asset extends MetadataType {
816
957
  encoding: 'base64',
817
958
  });
818
959
  }
960
+ const nestedFilePaths = [];
819
961
 
820
962
  // write to file (#1 + #2)
821
963
  const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir];
@@ -828,13 +970,22 @@ class Asset extends MetadataType {
828
970
  extractedFile.content,
829
971
  extractedFile.encoding || null
830
972
  );
973
+ nestedFilePaths.push([
974
+ File.normalizePath([targetDir, ...extractedFile.subFolder]),
975
+ extractedFile.fileName +
976
+ '.' +
977
+ this.definition.type +
978
+ '-meta.' +
979
+ extractedFile.fileExt,
980
+ ]);
831
981
  }
832
982
  }
983
+ return nestedFilePaths;
833
984
  }
834
985
  /**
835
986
  * generic script that retrieves the folder path from cache and updates the given metadata with it after retrieve
836
987
  *
837
- * @param {TYPE.MetadataTypeItem} metadata a single script activity definition
988
+ * @param {MetadataTypeItem} metadata a single script activity definition
838
989
  */
839
990
  static setFolderPath(metadata) {
840
991
  try {
@@ -856,7 +1007,7 @@ class Asset extends MetadataType {
856
1007
  /**
857
1008
  * Asset-specific script that retrieves the folder ID from cache and updates the given metadata with it before deploy
858
1009
  *
859
- * @param {TYPE.MetadataTypeItem} metadata a single item
1010
+ * @param {MetadataTypeItem} metadata a single item
860
1011
  */
861
1012
  static setFolderId(metadata) {
862
1013
  metadata.category = {
@@ -868,12 +1019,12 @@ class Asset extends MetadataType {
868
1019
  /**
869
1020
  * helper for {@link Asset.preDeployTasks} that loads extracted code content back into JSON
870
1021
  *
871
- * @param {TYPE.AssetItem} metadata a single asset definition
1022
+ * @param {AssetItem} metadata a single asset definition
872
1023
  * @param {string} deployDir directory of deploy files
873
- * @param {TYPE.AssetSubType} subType asset-subtype name
1024
+ * @param {string} subType asset-subtype name; full list in AssetSubType
874
1025
  * @param {string} [templateName] name of the template used to built defintion (prior applying templating)
875
1026
  * @param {boolean} [fileListOnly] does not read file contents nor update metadata if true
876
- * @returns {Promise.<TYPE.CodeExtract[]>} fileList for templating (disregarded during deployment)
1027
+ * @returns {Promise.<CodeExtract[]>} fileList for templating (disregarded during deployment)
877
1028
  */
878
1029
  static async _mergeCode(metadata, deployDir, subType, templateName, fileListOnly = false) {
879
1030
  const subtypeExtension = `.${this.definition.type}-${subType}-meta`;
@@ -882,13 +1033,14 @@ class Asset extends MetadataType {
882
1033
  let readDirArr;
883
1034
  // unfortunately, asset's key can contain spaces at beginning/end which can break the file system when folders are created with it
884
1035
  const customerKey = metadata.customerKey.trim();
1036
+ const templateFileName = templateName || customerKey;
885
1037
  switch (metadata.assetType.name) {
886
1038
  case 'templatebasedemail': // message
887
1039
  case 'htmlemail': {
888
1040
  // message
889
1041
  // this complex type always creates its own subdir per asset
890
1042
  subDirArr = [this.definition.type, subType];
891
- readDirArr = [deployDir, ...subDirArr, templateName || customerKey];
1043
+ readDirArr = [deployDir, ...subDirArr, templateFileName];
892
1044
 
893
1045
  // metadata.views.html.content (mandatory)
894
1046
  // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs
@@ -910,13 +1062,78 @@ class Asset extends MetadataType {
910
1062
  if (templateName) {
911
1063
  // to use this method in templating, store a copy of the info in fileList
912
1064
  fileList.push({
913
- subFolder: [...subDirArr, customerKey],
1065
+ subFolder: [...subDirArr, templateFileName],
914
1066
  fileName: fileName,
915
1067
  fileExt: 'html',
916
1068
  content: metadata.views.html.content,
917
1069
  });
918
1070
  }
919
1071
  }
1072
+ // metadata.views.html.content (optional)
1073
+ // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs
1074
+ const fileNamePreheader = 'views.preheader.content' + subtypeExtension;
1075
+ if (
1076
+ (await File.pathExists(
1077
+ File.normalizePath([...readDirArr, `${fileNamePreheader}.amp`])
1078
+ )) &&
1079
+ metadata.views.preheader
1080
+ ) {
1081
+ if (!fileListOnly) {
1082
+ metadata.views.preheader.content = await File.readFilteredFilename(
1083
+ readDirArr,
1084
+ fileNamePreheader,
1085
+ 'amp'
1086
+ );
1087
+ }
1088
+
1089
+ if (templateName) {
1090
+ // to use this method in templating, store a copy of the info in fileList
1091
+ fileList.push({
1092
+ subFolder: [...subDirArr, templateFileName],
1093
+ fileName: fileNamePreheader,
1094
+ fileExt: 'amp',
1095
+ content: metadata.views.preheader.content,
1096
+ });
1097
+ }
1098
+ }
1099
+
1100
+ // metadata.views.text.content (optional)
1101
+ // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs
1102
+ const fileNameText = 'views.text.content' + subtypeExtension;
1103
+ if (
1104
+ (await File.pathExists(
1105
+ File.normalizePath([...readDirArr, `${fileNameText}.amp`])
1106
+ )) &&
1107
+ metadata.views.text
1108
+ ) {
1109
+ if (!fileListOnly) {
1110
+ metadata.views.text.content = await File.readFilteredFilename(
1111
+ readDirArr,
1112
+ fileNameText,
1113
+ 'amp'
1114
+ );
1115
+ }
1116
+
1117
+ if (templateName) {
1118
+ // to use this method in templating, store a copy of the info in fileList
1119
+ fileList.push({
1120
+ subFolder: [...subDirArr, templateFileName],
1121
+ fileName: fileNameText,
1122
+ fileExt: 'amp',
1123
+ content: metadata.views.text.content,
1124
+ });
1125
+ }
1126
+ } else if (metadata.views.text) {
1127
+ // ensure the text version gets generated from html
1128
+ metadata.views.text.data = {
1129
+ email: {
1130
+ options: {
1131
+ generateFrom: 'html',
1132
+ },
1133
+ },
1134
+ };
1135
+ metadata.views.text.generateFrom = 'html';
1136
+ }
920
1137
 
921
1138
  // metadata.views.html.slots.<>.blocks.<>.content (optional)
922
1139
  if (metadata?.views?.html?.slots) {
@@ -938,7 +1155,7 @@ class Asset extends MetadataType {
938
1155
  // template-template
939
1156
  // this complex type always creates its own subdir per asset
940
1157
  subDirArr = [this.definition.type, subType];
941
- readDirArr = [deployDir, ...subDirArr, templateName || customerKey];
1158
+ readDirArr = [deployDir, ...subDirArr, templateFileName];
942
1159
  const fileName = 'content' + subtypeExtension;
943
1160
 
944
1161
  const fileExtArr = ['html']; // eslint-disable-line no-case-declarations
@@ -959,7 +1176,7 @@ class Asset extends MetadataType {
959
1176
  if (templateName) {
960
1177
  // to use this method in templating, store a copy of the info in fileList
961
1178
  fileList.push({
962
- subFolder: subDirArr,
1179
+ subFolder: [...subDirArr, templateFileName],
963
1180
  fileName: fileName,
964
1181
  fileExt: ext,
965
1182
  content: metadata.content,
@@ -996,7 +1213,7 @@ class Asset extends MetadataType {
996
1213
  await File.pathExists(
997
1214
  File.normalizePath([
998
1215
  ...readDirArr,
999
- `${templateName || customerKey}${subtypeExtension}.html`,
1216
+ `${templateFileName}${subtypeExtension}.amp`,
1000
1217
  ])
1001
1218
  )
1002
1219
  ) {
@@ -1004,16 +1221,16 @@ class Asset extends MetadataType {
1004
1221
  if (!fileListOnly) {
1005
1222
  metadata.views.text.content = await File.readFilteredFilename(
1006
1223
  readDirArr,
1007
- (templateName || customerKey) + subtypeExtension,
1008
- 'html'
1224
+ templateFileName + subtypeExtension,
1225
+ 'amp'
1009
1226
  );
1010
1227
  }
1011
1228
  if (templateName) {
1012
1229
  // to use this method in templating, store a copy of the info in fileList
1013
1230
  fileList.push({
1014
1231
  subFolder: subDirArr,
1015
- fileName: customerKey + subtypeExtension,
1016
- fileExt: 'html',
1232
+ fileName: templateFileName + subtypeExtension,
1233
+ fileExt: 'amp',
1017
1234
  content: metadata.views.text.content,
1018
1235
  });
1019
1236
  }
@@ -1024,7 +1241,7 @@ class Asset extends MetadataType {
1024
1241
  // asset
1025
1242
  // this complex type always creates its own subdir per asset
1026
1243
  subDirArr = [this.definition.type, subType];
1027
- readDirArr = [deployDir, ...subDirArr, templateName || customerKey];
1244
+ readDirArr = [deployDir, ...subDirArr, templateFileName];
1028
1245
 
1029
1246
  // metadata.views.html.slots.<>.blocks.<>.content (optional) (pre & post 20222)
1030
1247
  if (metadata?.views?.html?.slots) {
@@ -1062,7 +1279,7 @@ class Asset extends MetadataType {
1062
1279
  if (templateName) {
1063
1280
  // to use this method in templating, store a copy of the info in fileList
1064
1281
  fileList.push({
1065
- subFolder: [...subDirArr, customerKey],
1282
+ subFolder: [...subDirArr, templateFileName],
1066
1283
  fileName: 'views.html.content' + subtypeExtension,
1067
1284
  fileExt: 'html',
1068
1285
  content: metadata.views.html.content,
@@ -1088,7 +1305,7 @@ class Asset extends MetadataType {
1088
1305
  if (templateName) {
1089
1306
  // to use this method in templating, store a copy of the info in fileList
1090
1307
  fileList.push({
1091
- subFolder: [...subDirArr, customerKey],
1308
+ subFolder: [...subDirArr, templateFileName],
1092
1309
  fileName: 'content' + subtypeExtension,
1093
1310
  fileExt: 'html',
1094
1311
  content: metadata.views.html.content,
@@ -1116,7 +1333,7 @@ class Asset extends MetadataType {
1116
1333
  await File.pathExists(
1117
1334
  File.normalizePath([
1118
1335
  ...readDirArr,
1119
- `${templateName || customerKey}${subtypeExtension}.${ext}`,
1336
+ `${templateFileName}${subtypeExtension}.${ext}`,
1120
1337
  ])
1121
1338
  )
1122
1339
  ) {
@@ -1124,7 +1341,7 @@ class Asset extends MetadataType {
1124
1341
  if (!fileListOnly) {
1125
1342
  metadata.content = await File.readFilteredFilename(
1126
1343
  readDirArr,
1127
- (templateName || customerKey) + subtypeExtension,
1344
+ templateFileName + subtypeExtension,
1128
1345
  ext
1129
1346
  );
1130
1347
  if (ext === 'ssjs') {
@@ -1135,7 +1352,7 @@ class Asset extends MetadataType {
1135
1352
  // to use this method in templating, store a copy of the info in fileList
1136
1353
  fileList.push({
1137
1354
  subFolder: subDirArr,
1138
- fileName: (templateName || customerKey) + subtypeExtension,
1355
+ fileName: templateFileName + subtypeExtension,
1139
1356
  fileExt: ext,
1140
1357
  content: metadata.content,
1141
1358
  });
@@ -1174,6 +1391,8 @@ class Asset extends MetadataType {
1174
1391
  templateName,
1175
1392
  fileListOnly = false
1176
1393
  ) {
1394
+ const templateFileName = templateName || customerKey;
1395
+
1177
1396
  for (const slot in metadataSlots) {
1178
1397
  if (Object.prototype.hasOwnProperty.call(metadataSlots, slot)) {
1179
1398
  const slotObj = metadataSlots[slot];
@@ -1203,7 +1422,7 @@ class Asset extends MetadataType {
1203
1422
  if (templateName) {
1204
1423
  // to use this method in templating, store a copy of the info in fileList
1205
1424
  fileList.push({
1206
- subFolder: [...subDirArr, customerKey, 'blocks'],
1425
+ subFolder: [...subDirArr, templateFileName, 'blocks'],
1207
1426
  fileName: fileName,
1208
1427
  fileExt: 'html',
1209
1428
  content: slotObj.blocks[block].content,
@@ -1233,8 +1452,8 @@ class Asset extends MetadataType {
1233
1452
  * helper for {@link Asset.postRetrieveTasks} that finds code content in JSON and extracts it
1234
1453
  * to allow saving that separately and formatted
1235
1454
  *
1236
- * @param {TYPE.AssetItem} metadata a single asset definition
1237
- * @returns {TYPE.CodeExtractItem} { json: metadata, codeArr: object[], subFolder: string[] }
1455
+ * @param {AssetItem} metadata a single asset definition
1456
+ * @returns {CodeExtractItem} { json: metadata, codeArr: object[], subFolder: string[] }
1238
1457
  */
1239
1458
  static _extractCode(metadata) {
1240
1459
  const codeArr = [];
@@ -1255,6 +1474,27 @@ class Asset extends MetadataType {
1255
1474
  });
1256
1475
  delete metadata.views.html.content;
1257
1476
  }
1477
+ // metadata.views.preheader.content (optional)
1478
+ if (metadata.views?.preheader?.content?.length) {
1479
+ codeArr.push({
1480
+ subFolder: null,
1481
+ fileName: 'views.preheader.content',
1482
+ fileExt: 'amp',
1483
+ content: metadata.views.preheader.content,
1484
+ });
1485
+ delete metadata.views.preheader.content;
1486
+ }
1487
+
1488
+ // metadata.views.text.content (optional)
1489
+ if (metadata.views?.text?.content?.length) {
1490
+ codeArr.push({
1491
+ subFolder: null,
1492
+ fileName: 'views.text.content',
1493
+ fileExt: 'amp',
1494
+ content: metadata.views.text.content,
1495
+ });
1496
+ delete metadata.views.text.content;
1497
+ }
1258
1498
 
1259
1499
  // metadata.views.html.slots.<>.blocks.<>.content (optional)
1260
1500
  if (metadata.views?.html?.slots) {
@@ -1298,7 +1538,7 @@ class Asset extends MetadataType {
1298
1538
  codeArr.push({
1299
1539
  subFolder: null,
1300
1540
  fileName: customerKey,
1301
- fileExt: 'html',
1541
+ fileExt: 'amp',
1302
1542
  content: metadata.views.text.content,
1303
1543
  });
1304
1544
  delete metadata.views.text.content;
@@ -1429,11 +1669,11 @@ class Asset extends MetadataType {
1429
1669
  * Returns file contents mapped to their fileName without '.json' ending
1430
1670
  *
1431
1671
  * @param {string} dir directory that contains '.json' files to be read
1432
- * @param {void} [_] unused parameter
1672
+ * @param {boolean} _ unused parameter
1433
1673
  * @param {string[]} selectedSubType asset, message, ...
1434
- * @returns {TYPE.MetadataTypeMap} fileName => fileContent map
1674
+ * @returns {Promise.<MetadataTypeMap>} fileName => fileContent map
1435
1675
  */
1436
- static getJsonFromFS(dir, _, selectedSubType) {
1676
+ static async getJsonFromFS(dir, _, selectedSubType) {
1437
1677
  const fileName2FileContent = {};
1438
1678
  try {
1439
1679
  for (const subtype of this.definition.subTypes) {
@@ -1445,8 +1685,8 @@ class Asset extends MetadataType {
1445
1685
  continue;
1446
1686
  }
1447
1687
  const currentdir = File.normalizePath([dir, subtype]);
1448
- if (File.pathExistsSync(currentdir)) {
1449
- const files = File.readdirSync(currentdir, { withFileTypes: true });
1688
+ if (await File.pathExists(currentdir)) {
1689
+ const files = await File.readdir(currentdir, { withFileTypes: true });
1450
1690
 
1451
1691
  for (const dirent of files) {
1452
1692
  try {
@@ -1456,7 +1696,7 @@ class Asset extends MetadataType {
1456
1696
  // complex types with more than one extracted piece of code are saved in their
1457
1697
  // own subfolder (with folder name = CustomerKey)
1458
1698
  // this section aims to find that json in the subfolder
1459
- const subfolderFiles = File.readdirSync(
1699
+ const subfolderFiles = await File.readdir(
1460
1700
  File.normalizePath([currentdir, dirent.name])
1461
1701
  );
1462
1702
  for (const subFileName of subfolderFiles) {
@@ -1467,10 +1707,9 @@ class Asset extends MetadataType {
1467
1707
  }
1468
1708
  }
1469
1709
  if (fileName.endsWith('-meta.json')) {
1470
- const fileContent = File.readJSONFile(
1710
+ const fileContent = await File.readJSONFile(
1471
1711
  thisDir,
1472
1712
  fileName,
1473
- true,
1474
1713
  false
1475
1714
  );
1476
1715
  // subtype will change the metadata suffix length
@@ -1488,7 +1727,7 @@ class Asset extends MetadataType {
1488
1727
  }
1489
1728
  }
1490
1729
  } catch (ex) {
1491
- // this will catch issues with readdirSync
1730
+ // this will catch issues with readdir
1492
1731
  Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex);
1493
1732
  throw new Error(ex);
1494
1733
  }
@@ -1499,7 +1738,7 @@ class Asset extends MetadataType {
1499
1738
  *
1500
1739
  * @param {string} templateDir Directory where metadata templates are stored
1501
1740
  * @param {string} templateName name of the metadata file
1502
- * @returns {Promise.<TYPE.AssetSubType>} subtype name
1741
+ * @returns {Promise.<string>} AssetSubType name
1503
1742
  */
1504
1743
  static async findSubType(templateDir, templateName) {
1505
1744
  const typeDirArr = [this.definition.type];
@@ -1518,14 +1757,6 @@ class Asset extends MetadataType {
1518
1757
  break;
1519
1758
  }
1520
1759
  }
1521
- if (!subType) {
1522
- throw new Error(
1523
- `Could not find asset with name ${templateName} in ${File.normalizePath([
1524
- templateDir,
1525
- ...typeDirArr,
1526
- ])}`
1527
- );
1528
- }
1529
1760
  return subType;
1530
1761
  }
1531
1762
  /**
@@ -1535,7 +1766,7 @@ class Asset extends MetadataType {
1535
1766
  * @param {string[]} typeDirArr current subdir for this type
1536
1767
  * @param {string} templateName name of the metadata template
1537
1768
  * @param {string} fileName name of the metadata template file w/o extension
1538
- * @returns {TYPE.AssetItem} metadata
1769
+ * @returns {Promise.<string>} metadata in string form
1539
1770
  */
1540
1771
  static async readSecondaryFolder(templateDir, typeDirArr, templateName, fileName) {
1541
1772
  // handles subtypes that create 1 folder per asset -> currently causes the below File.ReadFile to error out
@@ -1547,7 +1778,7 @@ class Asset extends MetadataType {
1547
1778
  * additionally, the documentation for dataExtension and automation should be returned
1548
1779
  *
1549
1780
  * @param {string[]} keyArr customerkey of the metadata
1550
- * @returns {string[]} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
1781
+ * @returns {Promise.<string[]>} list of all files that need to be committed in a flat array ['path/file1.ext', 'path/file2.ext']
1551
1782
  */
1552
1783
  static async getFilesToCommit(keyArr) {
1553
1784
  const basePath = File.normalizePath([
@@ -1594,7 +1825,7 @@ class Asset extends MetadataType {
1594
1825
  (await File.pathExists(File.normalizePath([...filePath, fileName])))
1595
1826
  ) {
1596
1827
  // #1 load json to be able to find extracted text files & binary files
1597
- const metadata = File.readJSONFile(filePath, fileName, true, false);
1828
+ const metadata = await File.readJSONFile(filePath, fileName, false);
1598
1829
  // #2 find all extracted text files
1599
1830
  const fileListNested = (
1600
1831
  await this._mergeCode(metadata, basePath, subType, metadata.customerKey)
@@ -1629,6 +1860,427 @@ class Asset extends MetadataType {
1629
1860
  ).flat();
1630
1861
  return fileList;
1631
1862
  }
1863
+ /**
1864
+ * helper to allow us to select single metadata entries via REST
1865
+ *
1866
+ * @private
1867
+ * @param {string} key customer key
1868
+ * @returns {Promise.<string>} id value or null
1869
+ */
1870
+ static async _getIdForSingleRetrieve(key) {
1871
+ const name = key.startsWith('name:') ? key.slice(5) : null;
1872
+ const filter = name
1873
+ ? '?$filter=name%20eq%20' + encodeURIComponent(name)
1874
+ : '?$filter=customerKey%20eq%20' + encodeURIComponent(key);
1875
+
1876
+ const results = await this.client.rest.get('/asset/v1/content/assets/' + filter);
1877
+ const items = results?.items || [];
1878
+ const found = items.find((item) =>
1879
+ name ? item[this.definition.nameField] === name : item[this.definition.keyField] === key
1880
+ );
1881
+ return found?.id || null;
1882
+ }
1883
+ /**
1884
+ * helper to allow us to select single metadata entries via REST
1885
+ *
1886
+ * @private
1887
+ * @param {string} id id field
1888
+ * @returns {Promise.<string>} key value or null
1889
+ */
1890
+ static async _getKeyForSingleRetrieve(id) {
1891
+ const results = await this.client.rest.get('/asset/v1/content/assets/' + id);
1892
+ return results?.customerKey || null;
1893
+ }
1894
+
1895
+ /**
1896
+ * Delete a metadata item from the specified business unit
1897
+ *
1898
+ * @param {string} key Identifier of item
1899
+ * @returns {Promise.<boolean>} deletion success flag
1900
+ */
1901
+ static async deleteByKey(key) {
1902
+ // delete only works with the query's object id
1903
+ let id;
1904
+ if (key?.startsWith('id:')) {
1905
+ id = key.slice(3);
1906
+ // we need to get the actual key so that postDeletetTasks know what to do
1907
+ key = await this._getKeyForSingleRetrieve(id);
1908
+ } else {
1909
+ id = key ? await this._getIdForSingleRetrieve(key) : null;
1910
+ }
1911
+ if (!id) {
1912
+ Util.logger.error(` - ${this.definition.type} not found`);
1913
+ return false;
1914
+ }
1915
+ return super.deleteByKeyREST('/asset/v1/content/assets/' + id, key);
1916
+ }
1917
+
1918
+ /**
1919
+ * clean up after deleting a metadata item
1920
+ * cannot use the generic method due to the complexity of how assets are saved to disk
1921
+ *
1922
+ * @param {string} key Identifier of metadata item
1923
+ * @returns {Promise.<void>} -
1924
+ */
1925
+ static async postDeleteTasks(key) {
1926
+ if (key?.startsWith('id:')) {
1927
+ // sad
1928
+ }
1929
+ const fileArr = await this.getFilesToCommit([key]);
1930
+
1931
+ // check if asset sits in its own folder
1932
+ const ownFolderIndex =
1933
+ fileArr[0].indexOf(key + '\\') > 0
1934
+ ? fileArr[0].indexOf(key + '\\')
1935
+ : fileArr[0].indexOf(key + '/');
1936
+ if (ownFolderIndex > 0) {
1937
+ fileArr.push(fileArr[0].slice(0, ownFolderIndex + key.length));
1938
+ }
1939
+
1940
+ for (const filePath of fileArr) {
1941
+ await File.remove(filePath);
1942
+ }
1943
+ }
1944
+ /**
1945
+ * get name & key for provided id
1946
+ *
1947
+ * @param {string} id Identifier of metadata
1948
+ * @returns {Promise.<{key:string, name:string, path:string, folder:string, mid:number, error:string, isShared:boolean}>} key, name and path of metadata; null if not found
1949
+ */
1950
+ static async resolveId(id) {
1951
+ let response;
1952
+ const json = {
1953
+ key: '',
1954
+ isShared: false,
1955
+ name: '',
1956
+ path: '',
1957
+ folder: '',
1958
+ mid: 0,
1959
+ legacyId: 0,
1960
+ error: '',
1961
+ sharedWith: [],
1962
+ };
1963
+ try {
1964
+ response = await this.client.rest.get(
1965
+ 'asset/v1/content/assets/' +
1966
+ id +
1967
+ '?$fields=id,customerKey,name,memberId,legacyData,sharingProperties'
1968
+ );
1969
+ } catch (ex) {
1970
+ if (ex.response?.status !== 404) {
1971
+ throw ex;
1972
+ }
1973
+ }
1974
+ if (response?.id) {
1975
+ const item = response;
1976
+ const subType = this.#getMainSubtype(item.assetType.name);
1977
+
1978
+ // prep response object
1979
+ json.key = item[this.definition.keyField];
1980
+ json.name = item[this.definition.nameField];
1981
+ json.mid = item.memberId;
1982
+ json.isShared = item.memberId != this.buObject.mid;
1983
+ json.sharedWith = item.sharingProperties?.sharedWith || null;
1984
+ json.legacyId = item.legacyData?.legacyId;
1985
+
1986
+ const ownerBUName = json.isShared
1987
+ ? Util.inverseGet(
1988
+ this.properties.credentials[this.buObject.credential].businessUnits,
1989
+ json.mid
1990
+ )
1991
+ : this.buObject.businessUnit;
1992
+
1993
+ // find path for code of content block, fall back to json if not found; undefined if not even the json exists
1994
+ let path = await this.#getPath(subType, item, ownerBUName);
1995
+
1996
+ if (!json.sharedWith) {
1997
+ delete json.sharedWith;
1998
+ }
1999
+ if (!json.isShared && !path) {
2000
+ // if not shared, we should have the file on disk; attempt download
2001
+ if (!cache.getCache()?.folder) {
2002
+ // folders not cached yet but required to fill json.path
2003
+ Folder.client = this.client;
2004
+ Folder.buObject = this.buObject;
2005
+ Folder.properties = this.properties;
2006
+ const result = await Folder.retrieveForCache(null, ['asset', 'asset-shared']);
2007
+ if (!cache.getCache()) {
2008
+ cache.initCache(this.buObject);
2009
+ }
2010
+ cache.setMetadata('folder', result.metadata);
2011
+ }
2012
+
2013
+ await this.retrieve(
2014
+ File.normalizePath([
2015
+ this.properties.directories.retrieve,
2016
+ this.buObject.credential,
2017
+ this.buObject.businessUnit,
2018
+ ]),
2019
+ null,
2020
+ [subType],
2021
+ json.key
2022
+ );
2023
+ // try again
2024
+ path = await this.#getPath(subType, item, this.buObject.businessUnit);
2025
+ }
2026
+
2027
+ if (path) {
2028
+ json.path = path;
2029
+ delete json.error;
2030
+ } else {
2031
+ json.error = 'file not found on local disk';
2032
+ delete json.path;
2033
+ }
2034
+ const fileContent = await this.#getJson(subType, item);
2035
+ if (fileContent?.r__folder_Path) {
2036
+ json.folder = fileContent.r__folder_Path;
2037
+ }
2038
+ if (!json.legacyId) {
2039
+ delete json.legacyId;
2040
+ }
2041
+
2042
+ if (Util.OPTIONS.json) {
2043
+ // for automated processing by VSCode extension, optionally print the json
2044
+ console.log(JSON.stringify(json, null, 2)); // eslint-disable-line no-console
2045
+ } else {
2046
+ Util.logger.info(` - ID: ${id}`);
2047
+ Util.logger.info(` - Key: ${item[this.definition.keyField]}`);
2048
+ Util.logger.info(` - Name: ${item[this.definition.nameField]}`);
2049
+
2050
+ if (json.isShared) {
2051
+ Util.logger.warn(' - Shared from: ' + ownerBUName + ' (' + json.mid + ')');
2052
+ }
2053
+ if (json.sharedWith && Array.isArray(json.sharedWith)) {
2054
+ Util.logger.warn(
2055
+ ` with: ${json.sharedWith
2056
+ .map(
2057
+ (mid) =>
2058
+ Util.inverseGet(
2059
+ this.properties.credentials[this.buObject.credential]
2060
+ .businessUnits,
2061
+ mid
2062
+ ) +
2063
+ ' (' +
2064
+ mid +
2065
+ ')'
2066
+ )
2067
+ .sort()
2068
+ .join(', ')}`
2069
+ );
2070
+ }
2071
+ if (json.legacyId) {
2072
+ Util.logger.info(` - Legacy ID: ${json.legacyId}`);
2073
+ }
2074
+ Util.logger.info(` - How to use:`);
2075
+ Util.logger.info(` %%= ContentBlockByKey("${json.key}") =%%`);
2076
+
2077
+ if (json.folder) {
2078
+ Util.logger.info(
2079
+ ` %%= ContentBlockByName("${json.folder.split('/').join('\\') + '\\' + json.name}") =%%`
2080
+ );
2081
+ if (json.folder.includes('&amp;') || json.name.includes('&amp;')) {
2082
+ Util.logger.warn(
2083
+ ` - ContentBlockByName will fail because your path or name contains the & or &amp; symbol. Please replace it with 'and' or remove it.`
2084
+ );
2085
+ }
2086
+ }
2087
+ Util.logger.info(
2088
+ ' - local link: ' +
2089
+ (path ||
2090
+ `404. Try running mcdev r ${this.buObject.credential}/${ownerBUName} ${this.definition.type}-${subType} ${item[this.definition.keyField]}`)
2091
+ );
2092
+ }
2093
+ return json;
2094
+ } else {
2095
+ json.error = 'id not found';
2096
+ delete json.key;
2097
+ delete json.mid;
2098
+ delete json.name;
2099
+ delete json.path;
2100
+ delete json.folder;
2101
+ delete json.isShared;
2102
+ delete json.legacyId;
2103
+ delete json.sharedWith;
2104
+ if (Util.OPTIONS.json) {
2105
+ console.log(JSON.stringify(json, null, 2)); // eslint-disable-line no-console
2106
+ return json;
2107
+ }
2108
+ Util.logger.error(` - ${this.definition.type} with id ${id} not found on BU`);
2109
+ return json;
2110
+ }
2111
+ }
2112
+
2113
+ /**
2114
+ * helper for {@link Asset.resolveId} that finds the path to the asset's code
2115
+ *
2116
+ * @param {string} subType asset subtype
2117
+ * @param {object} item api response for metadata
2118
+ * @param {string} buName owner business unit name
2119
+ * @returns {string} path to the asset's code
2120
+ */
2121
+ static async #getPath(subType, item, buName) {
2122
+ const pathBase1 = `./retrieve/${this.buObject.credential}/${buName}/${this.definition.type}/${subType}/${item[this.definition.keyField]}.${this.definition.type}-${subType}-meta.`;
2123
+ const pathBase2 = `./retrieve/${this.buObject.credential}/${buName}/${this.definition.type}/${subType}/${item[this.definition.keyField]}/${item[this.definition.keyField]}.${this.definition.type}-${subType}-meta.`;
2124
+ const paths = [];
2125
+ for (const ext of ['html', 'ssjs', 'amp', 'json']) {
2126
+ paths.push(pathBase1 + ext, pathBase2 + ext);
2127
+ }
2128
+ const path = await Util.findAsync(paths, async (p) => await File.pathExists(p));
2129
+ return path;
2130
+ }
2131
+ /**
2132
+ * helper for {@link Asset.resolveId} that loads the JSON file for the asset
2133
+ *
2134
+ * @param {string} subType asset subtype
2135
+ * @param {object} item api response for metadata
2136
+ * @returns {Promise.<object>} JS object of the asset we loaded from disk
2137
+ */
2138
+ static async #getJson(subType, item) {
2139
+ const mid = item.memberId;
2140
+ const buName =
2141
+ mid === this.buObject.mid
2142
+ ? this.buObject.businessUnit
2143
+ : Object.keys(
2144
+ this.properties.credentials[this.buObject.credential].businessUnits
2145
+ ).find(
2146
+ (buName) =>
2147
+ this.properties.credentials[this.buObject.credential].businessUnits[
2148
+ buName
2149
+ ] == mid
2150
+ );
2151
+ const pathBase1 = `./retrieve/${this.buObject.credential}/${buName}/${this.definition.type}/${subType}/${item[this.definition.keyField]}.${this.definition.type}-${subType}-meta.`;
2152
+ const pathBase2 = `./retrieve/${this.buObject.credential}/${buName}/${this.definition.type}/${subType}/${item[this.definition.keyField]}/${item[this.definition.keyField]}.${this.definition.type}-${subType}-meta.`;
2153
+ const paths = [];
2154
+ paths.push(pathBase1 + 'json', pathBase2 + 'json');
2155
+
2156
+ const path = await Util.findAsync(paths, async (p) => await File.pathExists(p));
2157
+ const pathArr = path.split('/');
2158
+ const fileName = pathArr.pop().slice(0, -5);
2159
+ const fileContent = await File.readJSONFile(pathArr.join('/'), fileName, false);
2160
+ return fileContent;
2161
+ }
2162
+ /**
2163
+ *
2164
+ * @param {MetadataTypeItem} item single metadata item
2165
+ * @param {string} retrieveDir directory where metadata is saved
2166
+ * @returns {Promise.<MetadataTypeItem>} key of the item that was updated
2167
+ */
2168
+ static async replaceCbReference(item, retrieveDir) {
2169
+ const responseItem = structuredClone(item);
2170
+ const parentName = `${this.definition.type} ${item[this.definition.keyField]}`;
2171
+ let changes = false;
2172
+ let error;
2173
+
2174
+ // *** type specific logic ***
2175
+ const subType = this.#getMainSubtype(item.assetType.name);
2176
+ /** @type {CodeExtract[]} */
2177
+ const fileList = await this._mergeCode(
2178
+ item,
2179
+ retrieveDir,
2180
+ subType,
2181
+ item[this.definition.keyField],
2182
+ false
2183
+ );
2184
+ const fileListChanged = [];
2185
+ for (const file of fileList) {
2186
+ try {
2187
+ file.content = ReplaceCbReference.replaceReference(file.content, parentName);
2188
+ changes = true;
2189
+ fileListChanged.push(file);
2190
+ } catch (ex) {
2191
+ if (ex.code !== 200) {
2192
+ error = ex;
2193
+ }
2194
+ }
2195
+ }
2196
+
2197
+ // save what was changed regardless of other errors
2198
+ for (const extractedFile of fileListChanged) {
2199
+ File.writeToFile(
2200
+ [retrieveDir, ...extractedFile.subFolder],
2201
+ extractedFile.fileName,
2202
+ extractedFile.fileExt,
2203
+ extractedFile.content,
2204
+ extractedFile.encoding || null
2205
+ );
2206
+ }
2207
+
2208
+ if (error) {
2209
+ throw error;
2210
+ }
2211
+
2212
+ if (!changes) {
2213
+ const ex = new Error('No changes made to the code.');
2214
+ ex.code = 200;
2215
+ throw ex;
2216
+ }
2217
+
2218
+ // *** finish ***
2219
+ // replaceReference will throw an error if nothing was updated which will end execution here
2220
+ // no error means we have a new item to deploy and need to update the item in our retrieve folder
2221
+ return responseItem;
2222
+ }
2223
+ /**
2224
+ * this iterates over all items found in the retrieve folder and executes the type-specific method for replacing references
2225
+ *
2226
+ * @param {MetadataTypeMap} metadataMap list of metadata (keyField => metadata)
2227
+ * @param {string} retrieveDir retrieve dir including cred and bu
2228
+ * @returns {Promise.<string[]>} Returns list of keys for which references were replaced
2229
+ */
2230
+ static async replaceCbReferenceLoop(metadataMap, retrieveDir) {
2231
+ const keysForDeploy = [];
2232
+ if (!metadataMap) {
2233
+ // if a type was skipped e.g. because it shall only be looked at on the parent then we would expect metadataMap to be undefined
2234
+ return keysForDeploy;
2235
+ }
2236
+
2237
+ const fromDescription = Util.OPTIONS.referenceFrom
2238
+ .map((from) => 'ContentBlockBy' + Util.capitalizeFirstLetter(from))
2239
+ .join(' and ');
2240
+
2241
+ if (Object.keys(metadataMap).length) {
2242
+ Util.logger.info(`Searching ${this.definition.type} for ${fromDescription}:`);
2243
+ // const baseDir = [retrieveDir, ...this.definition.type.split('-')];
2244
+ const deployMap = {};
2245
+
2246
+ for (const key in metadataMap) {
2247
+ const item = metadataMap[key];
2248
+ if (this.isFiltered(item, true) || this.isFiltered(item, false)) {
2249
+ // we would not have saved these items to disk but they exist in the cache and hence need to be skipped here
2250
+
2251
+ continue;
2252
+ }
2253
+
2254
+ try {
2255
+ // add key but make sure to turn it into string or else numeric keys will be filtered later
2256
+ deployMap[key] = await this.replaceCbReference(item, retrieveDir);
2257
+
2258
+ // ! this method is equal to the super version except that it does not run saveToDisk here
2259
+ // await this.saveToDisk(deployMap, key, baseDir);
2260
+
2261
+ keysForDeploy.push(key + '');
2262
+ Util.logger.info(` - added ${this.definition.type} to update queue: ${key}`);
2263
+ } catch (ex) {
2264
+ if (ex.code !== 200) {
2265
+ // dont print error if we simply did not find relevant content blocks
2266
+ Util.logger.errorStack(ex, ex.message);
2267
+ }
2268
+ Util.logger.info(
2269
+ Util.getGrayMsg(
2270
+ ` ☇ skipping ${this.definition.type} ${
2271
+ item[this.definition.keyField]
2272
+ }: no ${fromDescription} found`
2273
+ )
2274
+ );
2275
+ }
2276
+ }
2277
+
2278
+ Util.logger.info(
2279
+ `Found ${keysForDeploy.length} ${this.definition.type}${keysForDeploy.length === 1 ? '' : 's'} to update`
2280
+ );
2281
+ }
2282
+ return keysForDeploy;
2283
+ }
1632
2284
  }
1633
2285
 
1634
2286
  // Assign definition to static attributes