Repository: Accenture/sfmc-devtools
Pull request #947 (2023-05-30 15:21:50)

5.0.2 
release


Patch:
diff --git a/.eslintrc.json b/.eslintrc.json
index c888eb65..355ff183 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -84,6 +84,7 @@
                 }
             }
         ],
+        "jsdoc/tag-lines": ["warn", "any", { "startLines": 1 }],
         "spaced-comment": ["warn", "always", { "block": { "exceptions": ["*"], "balanced": true } }]
     },
     "overrides": [
diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index 53b343b4..bca92801 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -39,6 +39,7 @@ body:
           label: Version
           description: What version of our software are you running? (mcdev --version)
           options:
+              - 5.0.2
               - 5.0.1
               - 5.0.0
               - 4.3.4
diff --git a/.husky/commit-msg b/.husky/commit-msg
index fdf5fdf8..cf766385 100644
--- a/.husky/commit-msg
+++ b/.husky/commit-msg
@@ -2,7 +2,7 @@
 . "$(dirname "$0")/_/husky.sh"
 INPUT_FILE=$1
 START_LINE=`head -n1 $INPUT_FILE`
-PATTERN="^(#[[:digit:]]|Merge)"
+PATTERN="^(#[[:digit:]]|Merge|Revert)"
 
 if ! [[ "$START_LINE" =~ $PATTERN ]] ; then
   echo "Bad commit message, see example: \"#431 commit message\", you provided: \"$START_LINE\""
diff --git a/.husky/post-checkout b/.husky/post-checkout
index 0f7e5707..e2201380 100644
--- a/.husky/post-checkout
+++ b/.husky/post-checkout
@@ -1,5 +1,36 @@
 #!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+# ### git commit message template ###
 git config commit.template .git/templatemessage
-TICKETID=`git rev-parse --abbrev-ref HEAD | LC_ALL=en_US.utf8 grep -oP '((feature|bug|bugfix|fix|hotfix|task|chore)\/)\K\d{1,7}'`
-echo "[POST_CHECKOUT] Setting template commit to $TICKETID"
-echo "#$TICKETID: " > ".git/templatemessage"
\ No newline at end of file
+TICKETID=`git rev-parse --abbrev-ref HEAD | LC_ALL=en_US.utf8 grep -oP '^((feature|bug|bugfix|fix|hotfix|task|chore)\/)?\K\d{1,7}' || true`
+if [ -z "$TICKETID" ]
+then
+  TICKETID="0"
+fi
+TEMPLATE="#$TICKETID: "
+echo "[POST_CHECKOUT] Setting template commit to '$TEMPLATE'"
+echo $TEMPLATE > ".git/templatemessage"
+
+
+# ### run npm install ###
+echo "[POST-CHECKOUT] 📦 Checking for changes to dependencies"
+# define how to split strings into array elements
+IFS=$'\n'
+# $1 is the new HEAD pointer
+NEWHEAD=$1
+# $2 is the previous HEAD pointer
+OLDHEAD=$2
+# extract all paths to package-lock.json files 
+PACKAGE_LOCK_REGEX="(^package-lock\.json)"
+PACKAGES=$(git diff --name-only $NEWHEAD $OLDHEAD | grep -E $PACKAGE_LOCK_REGEX || true)
+
+if [[ ${PACKAGES[@]} ]]; then
+  for package in $PACKAGES; do
+    echo "📦 $package was changed."
+  done
+  echo "📦 Running npm install to update your dependencies..."
+  npm install
+else
+  echo "📦 All packages up-to-date. No need to run npm install."
+fi
diff --git a/.husky/post-merge b/.husky/post-merge
new file mode 100644
index 00000000..06f668cd
--- /dev/null
+++ b/.husky/post-merge
@@ -0,0 +1,21 @@
+#!/bin/sh
+# ### run npm install ###
+. "$(dirname "$0")/_/husky.sh"
+
+
+echo "[POST-MERGE] 📦 Checking for changes to dependencies"
+
+IFS=$'\n'
+# extract all paths to package-lock.json files 
+PACKAGE_LOCK_REGEX="(^package-lock\.json)"
+PACKAGES=$(git diff --name-only HEAD^1 HEAD | grep -E $PACKAGE_LOCK_REGEX || true)
+
+if [[ ${PACKAGES[@]} ]]; then
+  for package in $PACKAGES; do
+    echo "📦 $package was changed."
+  done
+  echo "📦 Running npm install to update your dependencies..."
+  npm install
+else
+  echo "📦 All packages up-to-date. No need to run npm install."
+fi
\ No newline at end of file
diff --git a/.husky/pre-commit b/.husky/pre-commit
index d10908f8..28749003 100644
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,5 +1,6 @@
 #!/bin/sh
 . "$(dirname "$0")/_/husky.sh"
+
 npm run docs
 git update-index --add docs/dist/documentation.md
 npx --no lint-staged
diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md
index 586655ff..d9363cd3 100644
--- a/docs/dist/documentation.md
+++ b/docs/dist/documentation.md
@@ -1212,7 +1212,7 @@ Automation MetadataType
     * [.update(metadata, metadataBefore)](#Automation.update) ⇒ <code>Promise</code>
     * [.preDeployTasks(metadata)](#Automation.preDeployTasks) ⇒ <code>Promise.&lt;TYPE.AutomationItem&gt;</code>
     * [.validateDeployMetadata(metadata)](#Automation.validateDeployMetadata) ⇒ <code>boolean</code>
-    * [.postDeployTasks(metadata, originalMetadata)](#Automation.postDeployTasks) ⇒ <code>Promise.&lt;void&gt;</code>
+    * [.postDeployTasks(metadataMap, originalMetadataMap)](#Automation.postDeployTasks) ⇒ <code>Promise.&lt;void&gt;</code>
     * [.setFolderPath(metadata)](#Automation.setFolderPath)
     * [.setFolderId(metadata)](#Automation.setFolderId)
     * [.parseMetadata(metadata)](#Automation.parseMetadata) ⇒ <code>TYPE.AutomationItem</code> \| <code>void</code>
@@ -1345,7 +1345,7 @@ Whitelisted Activites are deployed but require configuration
 
 <a name="Automation.postDeployTasks"></a>
 
-### Automation.postDeployTasks(metadata, originalMetadata) ⇒ <code>Promise.&lt;void&gt;</code>
+### Automation.postDeployTasks(metadataMap, originalMetadataMap) ⇒ <code>Promise.&lt;void&gt;</code>
 Gets executed after deployment of metadata type
 
 **Kind**: static method of [<code>Automation</code>](#Automation)  
@@ -1353,8 +1353,8 @@ Gets executed after deployment of metadata type
 
 | Param | Type | Description |
 | --- | --- | --- |
-| metadata | <code>TYPE.AutomationMap</code> | metadata mapped by their keyField |
-| originalMetadata | <code>TYPE.AutomationMap</code> | metadata to be updated (contains additioanl fields) |
+| metadataMap | <code>TYPE.AutomationMap</code> | metadata mapped by their keyField |
+| originalMetadataMap | <code>TYPE.AutomationMap</code> | metadata to be updated (contains additioanl fields) |
 
 <a name="Automation.setFolderPath"></a>
 
@@ -3167,6 +3167,7 @@ Provides default functionality that can be overwritten by child metadata type cl
     * [.getSOAPErrorMsg(ex)](#MetadataType.getSOAPErrorMsg) ⇒ <code>string</code>
     * [.retrieveSOAP(retrieveDir, [requestParams], [singleRetrieve], [additionalFields])](#MetadataType.retrieveSOAP) ⇒ <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code>
     * [.retrieveREST(retrieveDir, uri, [templateVariables], [singleRetrieve])](#MetadataType.retrieveREST) ⇒ <code>Promise.&lt;{metadata: (TYPE.MetadataTypeMap\|TYPE.MetadataTypeItem), type: string}&gt;</code>
+    * [.runDocumentOnRetrieve([singleRetrieve], metadataMap)](#MetadataType.runDocumentOnRetrieve) ⇒ <code>Promise.&lt;void&gt;</code>
     * [.parseResponseBody(body, [singleRetrieve])](#MetadataType.parseResponseBody) ⇒ <code>TYPE.MetadataTypeMap</code>
     * [.deleteFieldByDefinition(metadataEntry, fieldPath, definitionProperty, origin)](#MetadataType.deleteFieldByDefinition) ⇒ <code>void</code>
     * [.removeNotCreateableFields(metadataEntry)](#MetadataType.removeNotCreateableFields) ⇒ <code>void</code>
@@ -3632,6 +3633,19 @@ Retrieves Metadata for Rest Types
 | [templateVariables] | <code>TYPE.TemplateMap</code> | variables to be replaced in the metadata |
 | [singleRetrieve] | <code>string</code> \| <code>number</code> | key of single item to filter by |
 
+<a name="MetadataType.runDocumentOnRetrieve"></a>
+
+### MetadataType.runDocumentOnRetrieve([singleRetrieve], metadataMap) ⇒ <code>Promise.&lt;void&gt;</code>
+helper for [retrieveREST](retrieveREST) and [retrieveSOAP](retrieveSOAP)
+
+**Kind**: static method of [<code>MetadataType</code>](#MetadataType)  
+**Returns**: <code>Promise.&lt;void&gt;</code> - -  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| [singleRetrieve] | <code>string</code> \| <code>number</code> | key of single item to filter by |
+| metadataMap | <code>TYPE.MetadataTypeMap</code> | saved metadata |
+
 <a name="MetadataType.parseResponseBody"></a>
 
 ### MetadataType.parseResponseBody(body, [singleRetrieve]) ⇒ <code>TYPE.MetadataTypeMap</code>
@@ -4006,11 +4020,13 @@ MobileKeyword MetadataType
 
 * [MobileKeyword](#MobileKeyword) ⇐ [<code>MetadataType</code>](#MetadataType)
     * [.retrieve(retrieveDir, [_], [__], [key])](#MobileKeyword.retrieve) ⇒ <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code> \| <code>void</code>
+    * [.parseResponseBody(body, [singleRetrieve])](#MobileKeyword.parseResponseBody) ⇒ <code>TYPE.MetadataTypeMap</code>
+    * [.createOrUpdate(metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate)](#MobileKeyword.createOrUpdate) ⇒ <code>&#x27;create&#x27;</code> \| <code>&#x27;update&#x27;</code> \| <code>&#x27;skip&#x27;</code>
     * [.retrieveForCache(_, __, [key])](#MobileKeyword.retrieveForCache) ⇒ <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code>
-    * [.retrieveAsTemplate(templateDir, name, templateVariables)](#MobileKeyword.retrieveAsTemplate) ⇒ <code>Promise.&lt;TYPE.MetadataTypeItemObj&gt;</code>
-    * [.create(MobileKeyword)](#MobileKeyword.create) ⇒ <code>Promise</code>
+    * [.retrieveAsTemplate(templateDir, key, templateVariables)](#MobileKeyword.retrieveAsTemplate) ⇒ <code>Promise.&lt;TYPE.MetadataTypeItemObj&gt;</code>
+    * [.create(metadata)](#MobileKeyword.create) ⇒ <code>Promise</code>
     * [.update(metadata)](#MobileKeyword.update) ⇒ <code>Promise</code>
-    * [.postRetrieveTasks(metadata)](#MobileKeyword.postRetrieveTasks) ⇒ <code>TYPE.CodeExtractItem</code> \| <code>TYPE.MetadataTypeItem</code>
+    * [.postRetrieveTasks(metadata)](#MobileKeyword.postRetrieveTasks) ⇒ <code>TYPE.CodeExtractItem</code> \| <code>TYPE.MetadataTypeItem</code> \| <code>void</code>
     * [.prepExtractedCode(metadataScript)](#MobileKeyword.prepExtractedCode) ⇒ <code>Object</code>
     * [.buildDefinitionForNested(templateDir, targetDir, metadata, templateVariables, templateName)](#MobileKeyword.buildDefinitionForNested) ⇒ <code>Promise.&lt;Array.&lt;Array.&lt;string&gt;&gt;&gt;</code>
     * [.buildTemplateForNested(templateDir, targetDir, metadata, templateVariables, templateName)](#MobileKeyword.buildTemplateForNested) ⇒ <code>Promise.&lt;Array.&lt;Array.&lt;string&gt;&gt;&gt;</code>
@@ -4039,6 +4055,35 @@ Endpoint /legacy/v1/beta/mobile/keyword/ return all Mobile Keywords with all det
 | [__] | <code>void</code> | unused parameter |
 | [key] | <code>string</code> | customer key of single item to retrieve |
 
+<a name="MobileKeyword.parseResponseBody"></a>
+
+### MobileKeyword.parseResponseBody(body, [singleRetrieve]) ⇒ <code>TYPE.MetadataTypeMap</code>
+Builds map of metadata entries mapped to their keyfields
+
+**Kind**: static method of [<code>MobileKeyword</code>](#MobileKeyword)  
+**Returns**: <code>TYPE.MetadataTypeMap</code> - keyField => metadata map  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| body | <code>object</code> | json of response body |
+| [singleRetrieve] | <code>string</code> \| <code>number</code> | key of single item to filter by |
+
+<a name="MobileKeyword.createOrUpdate"></a>
+
+### MobileKeyword.createOrUpdate(metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate) ⇒ <code>&#x27;create&#x27;</code> \| <code>&#x27;update&#x27;</code> \| <code>&#x27;skip&#x27;</code>
+helper for [upsert](#MetadataType.upsert)
+
+**Kind**: static method of [<code>MobileKeyword</code>](#MobileKeyword)  
+**Returns**: <code>&#x27;create&#x27;</code> \| <code>&#x27;update&#x27;</code> \| <code>&#x27;skip&#x27;</code> - action to take  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| metadataMap | <code>TYPE.MetadataTypeMap</code> | list of metadata |
+| metadataKey | <code>string</code> | key of item we are looking at |
+| hasError | <code>boolean</code> | error flag from previous code |
+| metadataToUpdate | <code>Array.&lt;TYPE.MetadataTypeItemDiff&gt;</code> | list of items to update |
+| metadataToCreate | <code>Array.&lt;TYPE.MetadataTypeItem&gt;</code> | list of items to create |
+
 <a name="MobileKeyword.retrieveForCache"></a>
 
 ### MobileKeyword.retrieveForCache(_, __, [key]) ⇒ <code>Promise.&lt;TYPE.MetadataTypeMapObj&gt;</code>
@@ -4055,8 +4100,8 @@ Retrieves event definition metadata for caching
 
 <a name="MobileKeyword.retrieveAsTemplate"></a>
 
-### MobileKeyword.retrieveAsTemplate(templateDir, name, templateVariables) ⇒ <code>Promise.&lt;TYPE.MetadataTypeItemObj&gt;</code>
-Retrieve a specific keyword
+### MobileKeyword.retrieveAsTemplate(templateDir, key, templateVariables) ⇒ <code>Promise.&lt;TYPE.MetadataTypeItemObj&gt;</code>
+retrieve an item and create a template from it
 
 **Kind**: static method of [<code>MobileKeyword</code>](#MobileKeyword)  
 **Returns**: <code>Promise.&lt;TYPE.MetadataTypeItemObj&gt;</code> - Promise of metadata  
@@ -4064,20 +4109,20 @@ Retrieve a specific keyword
 | Param | Type | Description |
 | --- | --- | --- |
 | templateDir | <code>string</code> | Directory where retrieved metadata directory will be saved |
-| name | <code>string</code> | name of the metadata file |
+| key | <code>string</code> | name of the metadata file |
 | templateVariables | <code>TYPE.TemplateMap</code> | variables to be replaced in the metadata |
 
 <a name="MobileKeyword.create"></a>
 
-### MobileKeyword.create(MobileKeyword) ⇒ <code>Promise</code>
-Creates a single Event Definition
+### MobileKeyword.create(metadata) ⇒ <code>Promise</code>
+Creates a single item
 
 **Kind**: static method of [<code>MobileKeyword</code>](#MobileKeyword)  
 **Returns**: <code>Promise</code> - Promise  
 
 | Param | Type | Description |
 | --- | --- | --- |
-| MobileKeyword | <code>TYPE.MetadataTypeItem</code> | a single Event Definition |
+| metadata | <code>TYPE.MetadataTypeItem</code> | a single item |
 
 <a name="MobileKeyword.update"></a>
 
@@ -4093,11 +4138,11 @@ Updates a single item
 
 <a name="MobileKeyword.postRetrieveTasks"></a>
 
-### MobileKeyword.postRetrieveTasks(metadata) ⇒ <code>TYPE.CodeExtractItem</code> \| <code>TYPE.MetadataTypeItem</code>
+### MobileKeyword.postRetrieveTasks(metadata) ⇒ <code>TYPE.CodeExtractItem</code> \| <code>TYPE.MetadataTypeItem</code> \| <code>void</code>
 manages post retrieve steps
 
 **Kind**: static method of [<code>MobileKeyword</code>](#MobileKeyword)  
-**Returns**: <code>TYPE.CodeExtractItem</code> \| <code>TYPE.MetadataTypeItem</code> - Array with one metadata object and one ssjs string  
+**Returns**: <code>TYPE.CodeExtractItem</code> \| <code>TYPE.MetadataTypeItem</code> \| <code>void</code> - Array with one metadata object and one ssjs string; or single metadata object; nothing if filtered  
 
 | Param | Type | Description |
 | --- | --- | --- |
diff --git a/lib/Deployer.js b/lib/Deployer.js
index 9aa8f802..29e267c2 100644
--- a/lib/Deployer.js
+++ b/lib/Deployer.js
@@ -18,7 +18,6 @@ class Deployer {
     /**
      * Creates a Deployer, uses v2 auth if v2AuthOptions are passed.
      *
-     *
      * @param {TYPE.Mcdevrc} properties General configuration to be used in retrieve
      * @param {TYPE.BuObject} buObject properties for auth
      */
diff --git a/lib/cli.js b/lib/cli.js
index d314a096..992d6f9f 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -240,7 +240,7 @@ yargs
         },
     })
     .command({
-        command: 'buildDefinition <BU> <TYPE> <NAME> <MARKET>',
+        command: 'buildDefinition <BU> <TYPE> <FILENAME> <MARKET>',
         aliases: ['bd'],
         desc: 'builds metadata definition based on template',
         builder: (yargs) => {
@@ -253,9 +253,9 @@ yargs
                     type: 'string',
                     describe: 'metadata type',
                 })
-                .positional('NAME', {
+                .positional('FILENAME', {
                     type: 'string',
-                    describe: 'name of the metadata component',
+                    describe: 'File name of the metadata template without the extension',
                 })
                 .positional('MARKET', {
                     type: 'string',
@@ -264,11 +264,11 @@ yargs
         },
         handler: (argv) => {
             Mcdev.setOptions(argv);
-            Mcdev.buildDefinition(argv.BU, argv.TYPE, argv.NAME, argv.MARKET);
+            Mcdev.buildDefinition(argv.BU, argv.TYPE, argv.FILENAME, argv.MARKET);
         },
     })
     .command({
-        command: 'buildDefinitionBulk <LISTNAME> <TYPE> <NAME>',
+        command: 'buildDefinitionBulk <LISTNAME> <TYPE> <FILENAME>',
         aliases: ['bdb'],
         desc: 'builds metadata definition based on template en bulk',
         builder: (yargs) => {
@@ -281,14 +281,14 @@ yargs
                     type: 'string',
                     describe: 'metadata type',
                 })
-                .positional('NAME', {
+                .positional('FILENAME', {
                     type: 'string',
-                    describe: 'name of the metadata component',
+                    describe: 'File name of the metadata template without the extension',
                 });
         },
         handler: (argv) => {
             Mcdev.setOptions(argv);
-            Mcdev.buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.NAME);
+            Mcdev.buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.FILENAME);
         },
     })
     .command({
diff --git a/lib/index.js b/lib/index.js
index 18b340c1..ecd28d29 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -256,11 +256,12 @@ class Mcdev {
             cred = buObject.credential;
             bu = buObject.businessUnit;
             // clean up old folders after types were renamed
-            // TODO: Remove this with version 5.0.0
+            // TODO: Remove renamedTypes-logic 6 months after version 5 release
             const renamedTypes = {
                 emailSend: 'emailSendDefinition',
                 event: 'eventDefinition',
                 fileLocation: 'ftpLocation',
+                journey: 'interaction',
                 triggeredSend: 'triggeredSendDefinition',
                 user: 'accountUser',
             };
diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js
index c4c37016..3ff861cc 100644
--- a/lib/metadataTypes/Asset.js
+++ b/lib/metadataTypes/Asset.js
@@ -26,7 +26,9 @@ class Asset extends MetadataType {
     static async retrieve(retrieveDir, _, subTypeArr, key) {
         const items = [];
         subTypeArr ||= this._getSubTypes();
-        await File.initPrettier();
+        if (retrieveDir) {
+            await File.initPrettier();
+        }
         // loop through subtypes and return results of each subType for caching (saving is handled per subtype)
         for (const subType of subTypeArr) {
             // each subtype contains multiple different specific types (images contains jpg and png for example)
@@ -235,7 +237,7 @@ class Asset extends MetadataType {
                     rightOperand: {
                         property: 'id',
                         simpleOperator: 'greaterThan',
-                        value: items[items.length - 1].id,
+                        value: items.at(-1).id,
                     },
                 };
                 lastPage = 0;
diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js
index 83d6b54d..8232a2e1 100644
--- a/lib/metadataTypes/Automation.js
+++ b/lib/metadataTypes/Automation.js
@@ -70,18 +70,18 @@ class Automation extends MetadataType {
                   })
               )
             : [];
-        const parsed = this.parseResponseBody({ items: details });
+        let metadataMap = this.parseResponseBody({ items: details });
+        // * retrieveDir can be empty when we use it in the context of postDeployTasks
+        if (retrieveDir) {
+            metadataMap = await this.saveResults(metadataMap, retrieveDir, null, null);
+            Util.logger.info(
+                `Downloaded: ${this.definition.type} (${Object.keys(metadataMap).length})` +
+                    Util.getKeysString(key)
+            );
 
-        // * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
-        const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
-        Util.logger.info(
-            `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
-                Util.getKeysString(key)
-        );
-        if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) {
-            await this.document(savedMetadata);
+            await this.runDocumentOnRetrieve(key, metadataMap);
         }
-        return { metadata: savedMetadata, type: this.definition.type };
+        return { metadata: metadataMap, type: this.definition.type };
     }
     /**
      * Retrieves Metadata of Automation
@@ -397,23 +397,23 @@ class Automation extends MetadataType {
     /**
      * Gets executed after deployment of metadata type
      *
-     * @param {TYPE.AutomationMap} metadata metadata mapped by their keyField
-     * @param {TYPE.AutomationMap} originalMetadata metadata to be updated (contains additioanl fields)
+     * @param {TYPE.AutomationMap} metadataMap metadata mapped by their keyField
+     * @param {TYPE.AutomationMap} originalMetadataMap metadata to be updated (contains additioanl fields)
      * @returns {Promise.<void>} -
      */
-    static async postDeployTasks(metadata, originalMetadata) {
-        for (const key in metadata) {
+    static async postDeployTasks(metadataMap, originalMetadataMap) {
+        for (const key in metadataMap) {
             // need to put schedule on here if status is scheduled
 
-            if (originalMetadata[key]?.type === 'scheduled') {
+            if (originalMetadataMap[key]?.type === 'scheduled') {
                 // Starting Source == 'Schedule': Try starting the automation
-                if (originalMetadata[key].status === 'Scheduled') {
+                if (originalMetadataMap[key].status === 'Scheduled') {
                     let schedule = null;
                     try {
-                        schedule = this._buildSchedule(originalMetadata[key].schedule);
+                        schedule = this._buildSchedule(originalMetadataMap[key].schedule);
                     } catch (ex) {
                         Util.logger.error(
-                            `- Could not create schedule for automation ${originalMetadata[key].name} to start it: ${ex.message}`
+                            `- Could not create schedule for automation ${originalMetadataMap[key].name} to start it: ${ex.message}`
                         );
                     }
                     if (schedule !== null) {
@@ -431,7 +431,7 @@ class Automation extends MetadataType {
                                 schedule,
                                 {
                                     Interaction: {
-                                        ObjectID: metadata[key].id,
+                                        ObjectID: metadataMap[key].id,
                                     },
                                 },
                                 'start',
@@ -445,31 +445,42 @@ class Automation extends MetadataType {
                                       (schedule_interval > 1 ? 's' : ''));
                             Util.logger.warn(
                                 ` - scheduled automation '${
-                                    originalMetadata[key].name
+                                    originalMetadataMap[key].name
                                 }' deployed Active: runs every ${intervalString} starting ${
                                     schedule_StartDateTime.split('T').join(' ').split('.')[0]
                                 } ${schedule_timezoneString}`
                             );
                         } catch (ex) {
                             Util.logger.error(
-                                `- Could not start scheduled automation '${originalMetadata[key].name}': ${ex.message}`
+                                `- Could not start scheduled automation '${originalMetadataMap[key].name}': ${ex.message}`
                             );
                         }
                     }
                 } else {
                     Util.logger.warn(
-                        ` - scheduled automation '${originalMetadata[key].name}' deployed Paused`
+                        ` - scheduled automation '${originalMetadataMap[key].name}' deployed Paused`
                     );
                 }
             }
-            if (metadata[key].startSource) {
-                metadata[key].schedule = metadata[key].startSource.schedule;
+            if (metadataMap[key].startSource) {
+                metadataMap[key].schedule = metadataMap[key].startSource.schedule;
 
-                delete metadata[key].startSource;
+                delete metadataMap[key].startSource;
             }
-            if (metadata[key].schedule) {
-                metadata[key].schedule.typeId = metadata[key].schedule.scheduleTypeId;
-                delete metadata[key].schedule.scheduleTypeId;
+            if (metadataMap[key].schedule) {
+                metadataMap[key].schedule.typeId = metadataMap[key].schedule.scheduleTypeId;
+                delete metadataMap[key].schedule.scheduleTypeId;
+            }
+
+            // re-retrieve deployed items because the API does not return any info for them except the new id (api key)
+            try {
+                const { metadata } = await this.retrieve(null, null, null, key);
+                metadataMap[key] = Object.values(metadata)[0];
+                // postRetrieveTasks will be run automatically on this via super.saveResult
+            } catch (ex) {
+                throw new Error(
+                    `Could not get details for new ${this.definition.type} ${key} from server (${ex.message})`
+                );
             }
         }
     }
diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js
index bfae1fdc..cd50d099 100644
--- a/lib/metadataTypes/DataExtension.js
+++ b/lib/metadataTypes/DataExtension.js
@@ -464,9 +464,7 @@ class DataExtension extends MetadataType {
                 `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
                     Util.getKeysString(key)
             );
-            if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) {
-                await this.document(savedMetadata);
-            }
+            await this.runDocumentOnRetrieve(key, savedMetadata);
         }
         return { metadata: metadata, type: 'dataExtension' };
     }
diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js
index c710650d..61b68d64 100644
--- a/lib/metadataTypes/DataExtensionField.js
+++ b/lib/metadataTypes/DataExtensionField.js
@@ -170,11 +170,11 @@ class DataExtensionField extends MetadataType {
 
                 // enable renaming
                 if (item.Name_new) {
+                    Util.logger.info(
+                        ` - Found Name_new='${item.Name_new}' for field ${deKey}.${item.Name} - trying to rename.`
+                    );
                     item.Name = item.Name_new;
                     delete item.Name_new;
-                    Util.logger.warn(
-                        ` - Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} - trying to rename.`
-                    );
                 }
 
                 // check if any changes were found
@@ -210,8 +210,8 @@ class DataExtensionField extends MetadataType {
                     }
                 }
                 if (item.Name_new) {
-                    Util.logger.warn(
-                        ` - Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} but could not find a corresponding DE field on the server - adding new field instead of updating.`
+                    Util.logger.info(
+                        ` - Found Name_new='${item.Name_new}' for field ${deKey}.${item.Name} but could not find a corresponding DE field on the server - adding new field instead of updating.`
                     );
                     delete item.Name_new;
                 }
diff --git a/lib/metadataTypes/Journey.js b/lib/metadataTypes/Journey.js
index 95fce8fe..7c20b2ee 100644
--- a/lib/metadataTypes/Journey.js
+++ b/lib/metadataTypes/Journey.js
@@ -542,12 +542,13 @@ class Journey extends MetadataType {
                     try {
                         // mobileKeyword
                         if (activity.configurationArguments?.keywordId) {
-                            activity.configurationArguments.c__mobileKeyword = cache.searchForField(
-                                'mobileKeyword',
-                                activity.configurationArguments.keywordId,
-                                'id',
-                                'keyword'
-                            );
+                            activity.configurationArguments.r__mobileKeyword_codeKeyword =
+                                cache.searchForField(
+                                    'mobileKeyword',
+                                    activity.configurationArguments.keywordId,
+                                    'id',
+                                    'c__codeKeyword'
+                                );
                             delete activity.configurationArguments.keywordId;
                         }
                     } catch (ex) {
@@ -833,14 +834,14 @@ class Journey extends MetadataType {
                         delete activity.configurationArguments.c__mobileMessage_id;
                     }
                     // mobileKeyword
-                    if (activity.configurationArguments?.c__mobileKeyword) {
+                    if (activity.configurationArguments?.r__mobileKeyword_codeKeyword) {
                         activity.configurationArguments.keywordId = cache.searchForField(
                             'mobileKeyword',
-                            activity.c__mobileKeyword,
-                            'keyword',
+                            activity.r__mobileKeyword_codeKeyword,
+                            'c__codeKeyword',
                             'id'
                         );
-                        delete activity.configurationArguments.c__mobileKeyword;
+                        delete activity.configurationArguments.r__mobileKeyword_codeKeyword;
                     }
                     if (activity.configurationArguments?.c__next_mobileKeyword) {
                         activity.configurationArguments.nextKeywordId = cache.searchForField(
diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js
index 80fe60d3..69b58259 100644
--- a/lib/metadataTypes/MetadataType.js
+++ b/lib/metadataTypes/MetadataType.js
@@ -1016,12 +1016,7 @@ class MetadataType {
                 `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
                     Util.getKeysString(singleRetrieve)
             );
-            if (
-                this.buObject &&
-                this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
-            ) {
-                await this.document(savedMetadata);
-            }
+            await this.runDocumentOnRetrieve(singleRetrieve, savedMetadata);
         }
         return { metadata: metadata, type: this.definition.type };
     }
@@ -1063,6 +1058,7 @@ class MetadataType {
                 `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
                     Util.getKeysString(singleRetrieve)
             );
+            await this.runDocumentOnRetrieve(singleRetrieve, savedMetadata);
         }
 
         return {
@@ -1071,6 +1067,34 @@ class MetadataType {
         };
     }
 
+    /**
+     * helper for {@link retrieveREST} and {@link retrieveSOAP}
+     *
+     * @param {string|number} [singleRetrieve] key of single item to filter by
+     * @param {TYPE.MetadataTypeMap} metadataMap saved metadata
+     * @returns {Promise.<void>} -
+     */
+    static async runDocumentOnRetrieve(singleRetrieve, metadataMap) {
+        if (
+            this.buObject &&
+            this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
+        ) {
+            if (!singleRetrieve || (singleRetrieve && !this.definition.documentInOneFile)) {
+                const count = Object.keys(metadataMap).length;
+                Util.logger.debug(
+                    ` - Running document for ${count} record${count === 1 ? '' : 's'}`
+                );
+                await this.document(metadataMap);
+            } else {
+                Util.logger.info(
+                    Util.getGrayMsg(
+                        ` - Skipped running document because you supplied keys and ${this.definition.type} is documented in a single file for all.`
+                    )
+                );
+            }
+        }
+    }
+
     /**
      * Builds map of metadata entries mapped to their keyfields
      *
@@ -1528,7 +1552,10 @@ class MetadataType {
                 // so its in retrieve but not in save. Here we put into the clone so that the original
                 // object used for caching doesnt have the Id removed.
                 const saveClone = JSON.parse(JSON.stringify(results[originalKey]));
-                if (!this.definition.keepId) {
+                if (
+                    !this.definition.keepId &&
+                    this.definition.idField !== this.definition.keyField
+                ) {
                     delete saveClone[this.definition.idField];
                 }
 
diff --git a/lib/metadataTypes/MobileKeyword.js b/lib/metadataTypes/MobileKeyword.js
index 4ccc7af4..02ade31c 100644
--- a/lib/metadataTypes/MobileKeyword.js
+++ b/lib/metadataTypes/MobileKeyword.js
@@ -24,14 +24,12 @@ class MobileKeyword extends MetadataType {
      */
     static retrieve(retrieveDir, _, __, key) {
         try {
+            let queryParams;
+            [key, queryParams] = this.#getRetrieveKeyAndUrl(key);
+
             return super.retrieveREST(
                 retrieveDir,
-                '/legacy/v1/beta/mobile/keyword/' +
-                    (key
-                        ? key.startsWith('id:')
-                            ? key.slice(3)
-                            : `?view=simple&$where=keyword%20eq%20%27${key}%27%20`
-                        : '?view=simple'),
+                '/legacy/v1/beta/mobile/keyword/' + queryParams,
                 null,
                 key
             );
@@ -47,6 +45,109 @@ class MobileKeyword extends MetadataType {
         }
     }
 
+    /**
+     * Builds map of metadata entries mapped to their keyfields
+     *
+     * @param {object} body json of response body
+     * @param {string|number} [singleRetrieve] key of single item to filter by
+     * @returns {TYPE.MetadataTypeMap} keyField => metadata map
+     */
+    static parseResponseBody(body, singleRetrieve) {
+        const bodyIteratorField = this.definition.bodyIteratorField;
+        const keyField = this.definition.keyField;
+        const metadataStructure = {};
+        if (body !== null) {
+            if (Array.isArray(body)) {
+                // in some cases data is just an array
+                for (const item of body) {
+                    this.#createCustomKeyField(item);
+                    const key = item[keyField];
+                    metadataStructure[key] = item;
+                }
+            } else if (body[bodyIteratorField]) {
+                for (const item of body[bodyIteratorField]) {
+                    this.#createCustomKeyField(item);
+                    const key = item[keyField];
+                    metadataStructure[key] = item;
+                }
+            } else if (singleRetrieve) {
+                // some types will return a single item intead of an array if the key is supported by their api
+                this.#createCustomKeyField(body);
+                // ! currently, the id: prefix is only supported by journey (interaction)
+                if (singleRetrieve.startsWith('id:')) {
+                    singleRetrieve = body[keyField];
+                }
+                metadataStructure[singleRetrieve] = body;
+                return metadataStructure;
+            }
+            if (
+                metadataStructure[singleRetrieve] &&
+                (typeof singleRetrieve === 'string' || typeof singleRetrieve === 'number')
+            ) {
+                // in case we really just wanted one entry but couldnt do so in the api call, filter it here
+                const single = {};
+                single[singleRetrieve] = metadataStructure[singleRetrieve];
+                return single;
+            } else if (singleRetrieve) {
+                return {};
+            }
+        }
+        return metadataStructure;
+    }
+
+    /**
+     * helper for {@link parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
+     *
+     * @private
+     * @param {TYPE.MetadataType} metadata single item
+     */
+    static #createCustomKeyField(metadata) {
+        metadata.c__codeKeyword = metadata.code.code + '.' + metadata.keyword;
+    }
+
+    /**
+     * helper for {@link preDeployTasks} and {@link createOrUpdate} to ensure we have code & keyword properly set
+     *
+     * @private
+     * @param {TYPE.MetadataType} metadata single item
+     */
+    static #setCodeAndKeyword(metadata) {
+        const [code, keyword] = metadata.c__codeKeyword.split('.');
+
+        // mobileCode
+        metadata.code = {
+            id: cache.searchForField('mobileCode', code, 'code', 'id'),
+        };
+
+        // keyword
+        metadata.keyword = keyword;
+    }
+
+    /**
+     * helper for {@link MetadataType.upsert}
+     *
+     * @param {TYPE.MetadataTypeMap} metadataMap list of metadata
+     * @param {string} metadataKey key of item we are looking at
+     * @param {boolean} hasError error flag from previous code
+     * @param {TYPE.MetadataTypeItemDiff[]} metadataToUpdate list of items to update
+     * @param {TYPE.MetadataTypeItem[]} metadataToCreate list of items to create
+     * @returns {'create' | 'update' | 'skip'} action to take
+     */
+    static createOrUpdate(metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate) {
+        const createOrUpdateAction = super.createOrUpdate(
+            metadataMap,
+            metadataKey,
+            hasError,
+            metadataToUpdate,
+            metadataToCreate
+        );
+        if (createOrUpdateAction === 'update') {
+            // in case --changeKeyField or --changeKeyValue was used, let's ensure we set code & keyword here again
+            this.#setCodeAndKeyword(metadataMap[metadataKey]);
+        }
+        return createOrUpdateAction;
+    }
+
     /**
      * Retrieves event definition metadata for caching
      *
@@ -60,26 +161,29 @@ class MobileKeyword extends MetadataType {
     }
 
     /**
-     * Retrieve a specific keyword
+     * retrieve an item and create a template from it
      *
      * @param {string} templateDir Directory where retrieved metadata directory will be saved
-     * @param {string} name name of the metadata file
+     * @param {string} key name of the metadata file
      * @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
      * @returns {Promise.<TYPE.MetadataTypeItemObj>} Promise of metadata
      */
-    static async retrieveAsTemplate(templateDir, name, templateVariables) {
+    static async retrieveAsTemplate(templateDir, key, templateVariables) {
         try {
+            let queryParams;
+            [key, queryParams] = this.#getRetrieveKeyAndUrl(key);
+
             return super.retrieveTemplateREST(
                 templateDir,
-                `/legacy/v1/beta/mobile/keyword/?view=simple&$where=keyword%20eq%20%27${name}%27%20`,
+                `/legacy/v1/beta/mobile/keyword/` + queryParams,
                 templateVariables,
-                name
+                key
             );
         } catch (ex) {
             // if the mobileMessage does not exist, the API returns the error "Request failed with status code 400 (ERR_BAD_REQUEST)" which would otherwise bring execution to a hold
-            if (name && ex.code === 'ERR_BAD_REQUEST') {
+            if (key && ex.code === 'ERR_BAD_REQUEST') {
                 Util.logger.info(
-                    `Downloaded: ${this.definition.type} (0)${Util.getKeysString(name)}`
+                    `Downloaded: ${this.definition.type} (0)${Util.getKeysString(key)}`
                 );
             } else {
                 throw ex;
@@ -88,13 +192,43 @@ class MobileKeyword extends MetadataType {
     }
 
     /**
-     * Creates a single Event Definition
+     * helper for {@link retrieve} and {@link retrieveAsTemplate}
+     *
+     * @private
+     * @param {string} key customer key of single item to retrieve / name of the metadata file
+     * @returns {Array} key, queryParams
+     */
+    static #getRetrieveKeyAndUrl(key) {
+        let queryParams;
+        if (key) {
+            if (key.startsWith('id:')) {
+                // overwrite queryParams
+                queryParams = key.slice(3);
+            } else if (key.includes('.')) {
+                // keywords are always uppercased
+                key = key.toUpperCase();
+                // format: code.keyword
+                const [code, keyword] = key.split('.');
+                queryParams = `?view=simple&$where=keyword%20eq%20%27${keyword}%27%20and%code%20eq%20%27${code}%27%20`;
+            } else {
+                throw new Error(
+                    `key ${key} has unexpected format. Expected 'code.keyword' or 'id:yourId'`
+                );
+            }
+        } else {
+            queryParams = '?view=simple';
+        }
+        return [key, queryParams];
+    }
+
+    /**
+     * Creates a single item
      *
-     * @param {TYPE.MetadataTypeItem} MobileKeyword a single Event Definition
+     * @param {TYPE.MetadataTypeItem} metadata a single item
      * @returns {Promise} Promise
      */
-    static create(MobileKeyword) {
-        return super.createREST(MobileKeyword, '/legacy/v1/beta/mobile/keyword/');
+    static create(metadata) {
+        return super.createREST(metadata, '/legacy/v1/beta/mobile/keyword/');
     }
     /**
      * Updates a single item
@@ -114,9 +248,21 @@ class MobileKeyword extends MetadataType {
      * manages post retrieve steps
      *
      * @param {TYPE.MetadataTypeItem} metadata a single item
-     * @returns {TYPE.CodeExtractItem | TYPE.MetadataTypeItem} Array with one metadata object and one ssjs string
+     * @returns {TYPE.CodeExtractItem | TYPE.MetadataTypeItem | void} Array with one metadata object and one ssjs string; or single metadata object; nothing if filtered
      */
     static postRetrieveTasks(metadata) {
+        try {
+            cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
+        } catch {
+            // in case the the mobileCode cannot be found, do not save this keyword as its no longer accessible in the UI either
+            Util.logger.debug(
+                ` - skipping ${this.definition.type} ${
+                    metadata[this.definition.keyField]
+                }. Could not find parent mobileCode ${metadata.code.code}`
+            );
+            return;
+        }
+
         if (metadata.responseMessage) {
             // extract message body
             const codeArr = [];
@@ -295,8 +441,7 @@ class MobileKeyword extends MetadataType {
             );
         }
 
-        // mobileCode
-        metadata.code.id = cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
+        this.#setCodeAndKeyword(metadata);
         return metadata;
     }
     /**
diff --git a/lib/metadataTypes/MobileMessage.js b/lib/metadataTypes/MobileMessage.js
index 0ad0f2b3..a9a1f822 100644
--- a/lib/metadataTypes/MobileMessage.js
+++ b/lib/metadataTypes/MobileMessage.js
@@ -163,21 +163,17 @@ class MobileMessage extends MetadataType {
 
         // mobileKeyword
         try {
-            if (metadata.keyword?.keyword) {
-                cache.searchForField(
-                    'mobileKeyword',
-                    metadata.keyword.keyword,
-                    'keyword',
-                    'keyword'
-                );
-            }
-            if (metadata.subscriptionKeyword?.keyword) {
-                cache.searchForField(
-                    'mobileKeyword',
-                    metadata.subscriptionKeyword.keyword,
-                    'keyword',
-                    'keyword'
-                );
+            for (const attr of ['keyword', 'subscriptionKeyword', 'nextKeyword']) {
+                if (metadata[attr]?.id) {
+                    metadata[attr] = {
+                        c__codeKeyword: cache.searchForField(
+                            'mobileKeyword',
+                            metadata[attr].id,
+                            'id',
+                            'c__codeKeyword'
+                        ),
+                    };
+                }
             }
         } catch (ex) {
             Util.logger.warn(
@@ -265,20 +261,16 @@ class MobileMessage extends MetadataType {
         metadata.code = code;
 
         // mobileKeyword
-        if (metadata.keyword?.keyword) {
-            const keyword = cache.getByKey('mobileKeyword', metadata.keyword.keyword);
-            if (!keyword) {
-                throw new Error(`mobileKeyword ${metadata.keyword.keyword} not found in cache`);
-            }
-            metadata.keyword = keyword;
-        }
-        if (metadata.subscriptionKeyword?.keyword) {
-            const keyword = cache.getByKey('mobileKeyword', metadata.subscriptionKeyword.keyword);
-            if (!keyword) {
-                throw new Error(`mobileKeyword ${metadata.keyword.keyword} not found in cache`);
+        for (const attr of ['keyword', 'subscriptionKeyword', 'nextKeyword']) {
+            if (metadata[attr]?.c__codeKeyword) {
+                const keywordObj = cache.getByKey('mobileKeyword', metadata[attr].c__codeKeyword);
+                if (!keywordObj) {
+                    throw new Error(
+                        `mobileKeyword ${metadata[attr].c__codeKeyword} not found in cache`
+                    );
+                }
+                metadata[attr] = keywordObj;
             }
-
-            metadata.subscriptionKeyword.keyword = keyword;
         }
 
         // campaign
diff --git a/lib/metadataTypes/Role.js b/lib/metadataTypes/Role.js
index d6d4d7c8..63e5d253 100644
--- a/lib/metadataTypes/Role.js
+++ b/lib/metadataTypes/Role.js
@@ -102,9 +102,8 @@ class Role extends MetadataType {
                 `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
                     Util.getKeysString(key)
             );
-            if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) {
-                await this.document(savedMetadata);
-            }
+
+            await this.runDocumentOnRetrieve(key, savedMetadata);
         }
         return { metadata: parsed, type: this.definition.type };
     }
diff --git a/lib/metadataTypes/TransactionalSMS.js b/lib/metadataTypes/TransactionalSMS.js
index 5b429c49..4e9bfb3d 100644
--- a/lib/metadataTypes/TransactionalSMS.js
+++ b/lib/metadataTypes/TransactionalSMS.js
@@ -58,9 +58,9 @@ class TransactionalSMS extends TransactionalMessage {
             // we merely want to be able to show an error if it does not exist
             cache.searchForField(
                 'mobileKeyword',
-                metadata.subscriptions.keyword,
-                'keyword',
-                'keyword'
+                metadata.subscriptions.shortCode + '.' + metadata.subscriptions.keyword,
+                'c__codeKeyword',
+                'id'
             );
         }
         return metadata;
@@ -142,9 +142,9 @@ class TransactionalSMS extends TransactionalMessage {
                 // we merely want to be able to show a warning if it does not exist
                 cache.searchForField(
                     'mobileKeyword',
-                    metadata.subscriptions.keyword,
+                    metadata.subscriptions.shortCode + '.' + metadata.subscriptions.keyword,
                     'keyword',
-                    'keyword'
+                    'id'
                 );
             } catch {
                 Util.logger.warn(
diff --git a/lib/metadataTypes/User.js b/lib/metadataTypes/User.js
index d53a3ff7..59701f08 100644
--- a/lib/metadataTypes/User.js
+++ b/lib/metadataTypes/User.js
@@ -215,12 +215,12 @@ class User extends MetadataType {
         );
 
         if (action === 'create') {
-            const createItem = metadataToCreate[metadataToCreate.length - 1];
+            const createItem = metadataToCreate.at(-1);
             User._setPasswordForNewUser(createItem);
             User._prepareRoleAssignments({ after: createItem });
             User._prepareBuAssignments(metadata[metadataKey], null, createItem);
         } else if (action === 'update') {
-            const updateItem = metadataToUpdate[metadataToUpdate.length - 1];
+            const updateItem = metadataToUpdate.at(-1);
             User._prepareRoleAssignments(updateItem);
             User._prepareBuAssignments(metadata[metadataKey], updateItem, null);
         }
@@ -731,13 +731,7 @@ class User extends MetadataType {
                     )
                 );
             }
-            if (
-                !singleRetrieve &&
-                this.buObject &&
-                this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
-            ) {
-                await this.document(savedMetadata);
-            }
+            await this.runDocumentOnRetrieve(singleRetrieve, savedMetadata);
         }
         return { metadata: metadata, type: this.definition.type };
     }
@@ -944,14 +938,6 @@ class User extends MetadataType {
             return;
         }
 
-        // if ran as part of retrieve/deploy with key, exit here
-        if (metadata && Object.keys(metadata).length === 1) {
-            Util.logger.debug(
-                'Only 1 user found. Skipping documentation, assuming we ran retrieve-by-key.'
-            );
-            return;
-        }
-
         if (!metadata) {
             // load users from disk if document was called directly and not part of a retrieve
             try {
diff --git a/lib/metadataTypes/definitions/MobileKeyword.definition.js b/lib/metadataTypes/definitions/MobileKeyword.definition.js
index 3f4ebd66..72ac918a 100644
--- a/lib/metadataTypes/definitions/MobileKeyword.definition.js
+++ b/lib/metadataTypes/definitions/MobileKeyword.definition.js
@@ -3,8 +3,8 @@ module.exports = {
     dependencies: ['mobileCode'],
     hasExtended: false,
     idField: 'id',
-    keyIsFixed: false,
-    keyField: 'keyword',
+    keyIsFixed: false, // custom field but mapped to normal fields
+    keyField: 'c__codeKeyword',
     nameField: 'keyword',
     createdDateField: 'createdDate',
     createdNameField: 'createdBy.name',
@@ -21,14 +21,20 @@ module.exports = {
             // irregular format: NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow
             isCreateable: false,
             isUpdateable: true,
-            retrieving: true,
+            retrieving: false,
             template: false,
         },
+        c__codeKeyword: {
+            isCreateable: false,
+            isUpdateable: false,
+            retrieving: true,
+            template: true,
+        },
         keyword: {
             isCreateable: true,
             isUpdateable: true,
-            retrieving: true,
-            template: true,
+            retrieving: false,
+            template: false,
         },
         createdDate: {
             isCreateable: false,
@@ -133,11 +139,17 @@ module.exports = {
             retrieving: true,
             template: false,
         },
+        code: {
+            isCreateable: true,
+            isUpdateable: true,
+            retrieving: false,
+            template: false,
+        },
         'code.code': {
             isCreateable: false,
             isUpdateable: false,
-            retrieving: true,
-            template: true,
+            retrieving: false,
+            template: false,
         },
         'code.id': {
             isCreateable: true,
diff --git a/lib/metadataTypes/definitions/MobileMessage.definition.js b/lib/metadataTypes/definitions/MobileMessage.definition.js
index 46344738..93bfc9ee 100644
--- a/lib/metadataTypes/definitions/MobileMessage.definition.js
+++ b/lib/metadataTypes/definitions/MobileMessage.definition.js
@@ -316,6 +316,12 @@ module.exports = {
         'keyword.keyword': {
             isCreateable: true,
             isUpdateable: true,
+            retrieving: false,
+            template: false,
+        },
+        'keyword.c__codeKeyword': {
+            isCreateable: false,
+            isUpdateable: false,
             retrieving: true,
             template: true,
         },
@@ -620,27 +626,63 @@ module.exports = {
         'subscriptionKeyword.id': {
             isCreatable: true,
             isUpdatable: true,
-            retrieving: true,
-            template: true,
+            retrieving: false,
+            template: false,
         },
         'subscriptionKeyword.keyword': {
-            isCreatable: true,
-            isUpdatable: true,
+            isCreatable: false,
+            isUpdatable: false,
+            retrieving: false,
+            template: false,
+        },
+        'subscriptionKeyword.c__codeKeyword': {
+            isCreateable: false,
+            isUpdateable: false,
             retrieving: true,
             template: true,
         },
         'subscriptionKeyword.restriction': {
-            isCreatable: true,
-            isUpdatable: true,
-            retrieving: true,
-            template: true,
+            isCreatable: false,
+            isUpdatable: false,
+            retrieving: false,
+            template: false,
         },
         'subscriptionKeyword.isInherited': {
+            isCreatable: false,
+            isUpdatable: false,
+            retrieving: false,
+            template: false,
+        },
+        'nextKeyword.id': {
             isCreatable: true,
             isUpdatable: true,
+            retrieving: false,
+            template: false,
+        },
+        'nextKeyword.keyword': {
+            isCreatable: false,
+            isUpdatable: false,
+            retrieving: false,
+            template: false,
+        },
+        'nextKeyword.c__codeKeyword': {
+            isCreateable: false,
+            isUpdateable: false,
             retrieving: true,
             template: true,
         },
+        'nextKeyword.restriction': {
+            isCreatable: false,
+            isUpdatable: false,
+            retrieving: false,
+            template: false,
+        },
+        'nextKeyword.isInherited': {
+            isCreatable: false,
+            isUpdatable: false,
+            retrieving: false,
+            template: false,
+        },
         subscriberResponseMessage: {
             isCreatable: true,
             isUpdatable: true,
diff --git a/lib/metadataTypes/definitions/User.definition.js b/lib/metadataTypes/definitions/User.definition.js
index adae97e1..34d3b5e0 100644
--- a/lib/metadataTypes/definitions/User.definition.js
+++ b/lib/metadataTypes/definitions/User.definition.js
@@ -17,6 +17,7 @@ module.exports = {
     typeDescription: 'Marketing Cloud users',
     typeName: 'User',
     typeRetrieveByDefault: false,
+    documentInOneFile: true,
     stringifyFieldsBeforeTemplate: ['DefaultBusinessUnit', 'c__AssociatedBusinessUnits'],
     fields: {
         AccountUserID: {
diff --git a/lib/util/auth.js b/lib/util/auth.js
index 34f4180b..1109b945 100644
--- a/lib/util/auth.js
+++ b/lib/util/auth.js
@@ -137,7 +137,10 @@ function setupSDK(sessionKey, authObject) {
                     Util.logger.debug('API REQUEST >> ' + JSON.stringify(msg, null, 2));
                     if (data) {
                         // printing it separately leads to better formatting
-                        Util.logger.debug('API REQUEST body >> \n  ' + data);
+                        Util.logger.debug(
+                            'API REQUEST body >> \n  ' +
+                                (typeof data === 'string' ? data : JSON.stringify(data, null, 2))
+                        );
                     }
                 }
             },
diff --git a/lib/util/cli.js b/lib/util/cli.js
index 0aa2d098..a1892779 100644
--- a/lib/util/cli.js
+++ b/lib/util/cli.js
@@ -570,7 +570,7 @@ const Cli = {
         }
         if (Util.OPTIONS.json) {
             if (Util.OPTIONS.loggerLevel !== 'error') {
-                console.log(json); // eslint-disable-line no-console
+                console.log(JSON.stringify(json, null, 2)); // eslint-disable-line no-console
             }
             return json;
         }
diff --git a/lib/util/file.js b/lib/util/file.js
index d4d9c694..89206f83 100644
--- a/lib/util/file.js
+++ b/lib/util/file.js
@@ -517,9 +517,10 @@ const File = {
      * @param {string} [filetype='html'] filetype ie. JSON or SSJS
      * @returns {Promise.<boolean>} success of config load
      */
-    async initPrettier(filetype) {
-        if (FileFs.prettierConfig === null) {
-            filetype ||= 'html';
+    async initPrettier(filetype = 'html') {
+        if (FileFs.prettierConfig === null || FileFs.prettierConfigFileType !== filetype) {
+            // run this if no config was yet found or if the filetype previously used to initialize it differs (because it results in a potentially different config!)
+            FileFs.prettierConfigFileType = filetype;
             try {
                 // pass in project dir with fake index.html to avoid "no parser" error
                 // by using process.cwd we are limiting ourselves to a config in the project root
@@ -547,5 +548,6 @@ const File = {
 };
 const FileFs = { ...fs, ...File };
 FileFs.prettierConfig = null;
+FileFs.prettierConfigFileType = null;
 
 module.exports = FileFs;
diff --git a/lib/util/util.js b/lib/util/util.js
index cc24c49b..482cbd0c 100644
--- a/lib/util/util.js
+++ b/lib/util/util.js
@@ -437,6 +437,7 @@ const Util = {
             }
         } catch {
             // avoid errors from execSync to bubble up
+            return null;
         }
     },
     /**
diff --git a/package-lock.json b/package-lock.json
index bd516725..cdc2e50d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "mcdev",
-    "version": "5.0.1",
+    "version": "5.0.2",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "mcdev",
-            "version": "5.0.1",
+            "version": "5.0.2",
             "license": "MIT",
             "dependencies": {
                 "beauty-amp-core": "0.3.7",
@@ -14,7 +14,7 @@
                 "command-exists": "1.2.9",
                 "conf": "10.2.0",
                 "console.table": "0.10.0",
-                "deep-equal": "2.1.0",
+                "deep-equal": "2.2.1",
                 "fs-extra": "11.1.0",
                 "inquirer": "8.2.5",
                 "json-to-table": "4.2.1",
@@ -23,12 +23,12 @@
                 "prettier": "2.8.8",
                 "prettier-plugin-sql": "0.14.0",
                 "semver": "7.5.0",
-                "sfmc-sdk": "1.0.0",
-                "simple-git": "3.16.0",
+                "sfmc-sdk": "1.0.1",
+                "simple-git": "3.18.0",
                 "toposort": "2.0.2",
                 "update-notifier": "5.1.0",
                 "winston": "3.8.2",
-                "yargs": "17.6.0"
+                "yargs": "17.7.2"
             },
             "bin": {
                 "mcdev": "lib/cli.js"
@@ -38,17 +38,17 @@
                 "axios-mock-adapter": "1.21.3",
                 "chai": "4.3.7",
                 "chai-files": "1.4.0",
-                "eslint": "8.39.0",
+                "eslint": "8.41.0",
                 "eslint-config-prettier": "8.7.0",
                 "eslint-config-ssjs": "1.1.11",
-                "eslint-plugin-jsdoc": "43.1.1",
+                "eslint-plugin-jsdoc": "45.0.0",
                 "eslint-plugin-mocha": "10.1.0",
                 "eslint-plugin-prettier": "4.2.1",
-                "eslint-plugin-unicorn": "46.0.0",
+                "eslint-plugin-unicorn": "47.0.0",
                 "fast-xml-parser": "4.2.2",
                 "husky": "8.0.3",
                 "jsdoc-to-markdown": "8.0.0",
-                "lint-staged": "13.2.0",
+                "lint-staged": "13.2.2",
                 "mocha": "10.2.0",
                 "mock-fs": "5.2.0",
                 "npm-check": "6.0.1",
@@ -507,9 +507,9 @@
             }
         },
         "node_modules/@es-joy/jsdoccomment": {
-            "version": "0.37.1",
-            "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.37.1.tgz",
-            "integrity": "sha512-5vxWJ1gEkEF0yRd0O+uK6dHJf7adrxwQSX8PuRiPfFSAbNLnY0ZJfXaZucoz14Jj2N11xn2DnlEPwWRpYpvRjg==",
+            "version": "0.39.4",
+            "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz",
+            "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==",
             "dev": true,
             "dependencies": {
                 "comment-parser": "1.3.1",
@@ -517,13 +517,13 @@
                 "jsdoc-type-pratt-parser": "~4.0.0"
             },
             "engines": {
-                "node": "^14 || ^16 || ^17 || ^18 || ^19 || ^20"
+                "node": ">=16"
             }
         },
         "node_modules/@eslint-community/eslint-utils": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz",
-            "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==",
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
             "dev": true,
             "dependencies": {
                 "eslint-visitor-keys": "^3.3.0"
@@ -545,14 +545,14 @@
             }
         },
         "node_modules/@eslint/eslintrc": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
-            "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz",
+            "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==",
             "dev": true,
             "dependencies": {
                 "ajv": "^6.12.4",
                 "debug": "^4.3.2",
-                "espree": "^9.5.1",
+                "espree": "^9.5.2",
                 "globals": "^13.19.0",
                 "ignore": "^5.2.0",
                 "import-fresh": "^3.2.1",
@@ -590,9 +590,9 @@
             "dev": true
         },
         "node_modules/@eslint/js": {
-            "version": "8.39.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
-            "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
+            "version": "8.41.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
+            "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
             "dev": true,
             "engines": {
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -1340,6 +1340,18 @@
                 "node": ">=12.17"
             }
         },
+        "node_modules/array-buffer-byte-length": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+            "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "is-array-buffer": "^3.0.1"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/array-differ": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz",
@@ -2642,25 +2654,28 @@
             }
         },
         "node_modules/deep-equal": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.1.0.tgz",
-            "integrity": "sha512-2pxgvWu3Alv1PoWEyVg7HS8YhGlUFUV7N5oOvfL6d+7xAmLSemMwv/c8Zv/i9KFzxV5Kt5CAvQc70fLwVuf4UA==",
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz",
+            "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==",
             "dependencies": {
+                "array-buffer-byte-length": "^1.0.0",
                 "call-bind": "^1.0.2",
-                "es-get-iterator": "^1.1.2",
-                "get-intrinsic": "^1.1.3",
+                "es-get-iterator": "^1.1.3",
+                "get-intrinsic": "^1.2.0",
                 "is-arguments": "^1.1.1",
+                "is-array-buffer": "^3.0.2",
                 "is-date-object": "^1.0.5",
                 "is-regex": "^1.1.4",
+                "is-shared-array-buffer": "^1.0.2",
                 "isarray": "^2.0.5",
                 "object-is": "^1.1.5",
                 "object-keys": "^1.1.1",
                 "object.assign": "^4.1.4",
-                "regexp.prototype.flags": "^1.4.3",
+                "regexp.prototype.flags": "^1.5.0",
                 "side-channel": "^1.0.4",
                 "which-boxed-primitive": "^1.0.2",
                 "which-collection": "^1.0.1",
-                "which-typed-array": "^1.1.8"
+                "which-typed-array": "^1.1.9"
             },
             "funding": {
                 "url": "https://github.com/sponsors/ljharb"
@@ -2718,9 +2733,9 @@
             "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
         },
         "node_modules/define-properties": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
-            "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+            "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
             "dependencies": {
                 "has-property-descriptors": "^1.0.0",
                 "object-keys": "^1.1.1"
@@ -3047,18 +3062,19 @@
             }
         },
         "node_modules/es-get-iterator": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
-            "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+            "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
             "dependencies": {
                 "call-bind": "^1.0.2",
-                "get-intrinsic": "^1.1.0",
-                "has-symbols": "^1.0.1",
-                "is-arguments": "^1.1.0",
+                "get-intrinsic": "^1.1.3",
+                "has-symbols": "^1.0.3",
+                "is-arguments": "^1.1.1",
                 "is-map": "^2.0.2",
                 "is-set": "^2.0.2",
-                "is-string": "^1.0.5",
-                "isarray": "^2.0.5"
+                "is-string": "^1.0.7",
+                "isarray": "^2.0.5",
+                "stop-iteration-iterator": "^1.0.0"
             },
             "funding": {
                 "url": "https://github.com/sponsors/ljharb"
@@ -3122,15 +3138,15 @@
             }
         },
         "node_modules/eslint": {
-            "version": "8.39.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
-            "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
+            "version": "8.41.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
+            "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
             "dev": true,
             "dependencies": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.4.0",
-                "@eslint/eslintrc": "^2.0.2",
-                "@eslint/js": "8.39.0",
+                "@eslint/eslintrc": "^2.0.3",
+                "@eslint/js": "8.41.0",
                 "@humanwhocodes/config-array": "^0.11.8",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@nodelib/fs.walk": "^1.2.8",
@@ -3141,8 +3157,8 @@
                 "doctrine": "^3.0.0",
                 "escape-string-regexp": "^4.0.0",
                 "eslint-scope": "^7.2.0",
-                "eslint-visitor-keys": "^3.4.0",
-                "espree": "^9.5.1",
+                "eslint-visitor-keys": "^3.4.1",
+                "espree": "^9.5.2",
                 "esquery": "^1.4.2",
                 "esutils": "^2.0.2",
                 "fast-deep-equal": "^3.1.3",
@@ -3150,13 +3166,12 @@
                 "find-up": "^5.0.0",
                 "glob-parent": "^6.0.2",
                 "globals": "^13.19.0",
-                "grapheme-splitter": "^1.0.4",
+                "graphemer": "^1.4.0",
                 "ignore": "^5.2.0",
                 "import-fresh": "^3.0.0",
                 "imurmurhash": "^0.1.4",
                 "is-glob": "^4.0.0",
                 "is-path-inside": "^3.0.3",
-                "js-sdsl": "^4.1.4",
                 "js-yaml": "^4.1.0",
                 "json-stable-stringify-without-jsonify": "^1.0.1",
                 "levn": "^0.4.1",
@@ -3206,18 +3221,18 @@
             }
         },
         "node_modules/eslint-plugin-jsdoc": {
-            "version": "43.1.1",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-43.1.1.tgz",
-            "integrity": "sha512-J2kjjsJ5vBXSyNzqJhceeSGTAgVgZHcPSJKo3vD4tNjUdfky98rR2VfZUDsS1GKL6isyVa8GWvr+Az7Vyg2HXA==",
+            "version": "45.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz",
+            "integrity": "sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g==",
             "dev": true,
             "dependencies": {
-                "@es-joy/jsdoccomment": "~0.37.1",
+                "@es-joy/jsdoccomment": "~0.39.4",
                 "are-docs-informative": "^0.0.2",
                 "comment-parser": "1.3.1",
                 "debug": "^4.3.4",
                 "escape-string-regexp": "^4.0.0",
                 "esquery": "^1.5.0",
-                "semver": "^7.5.0",
+                "semver": "^7.5.1",
                 "spdx-expression-parse": "^3.0.1"
             },
             "engines": {
@@ -3227,6 +3242,21 @@
                 "eslint": "^7.0.0 || ^8.0.0"
             }
         },
+        "node_modules/eslint-plugin-jsdoc/node_modules/semver": {
+            "version": "7.5.1",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
+            "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+            "dev": true,
+            "dependencies": {
+                "lru-cache": "^6.0.0"
+            },
+            "bin": {
+                "semver": "bin/semver.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
         "node_modules/eslint-plugin-mocha": {
             "version": "10.1.0",
             "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz",
@@ -3265,43 +3295,49 @@
             }
         },
         "node_modules/eslint-plugin-unicorn": {
-            "version": "46.0.0",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-46.0.0.tgz",
-            "integrity": "sha512-j07WkC+PFZwk8J33LYp6JMoHa1lXc1u6R45pbSAipjpfpb7KIGr17VE2D685zCxR5VL4cjrl65kTJflziQWMDA==",
+            "version": "47.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-47.0.0.tgz",
+            "integrity": "sha512-ivB3bKk7fDIeWOUmmMm9o3Ax9zbMz1Bsza/R2qm46ufw4T6VBFBaJIR1uN3pCKSmSXm8/9Nri8V+iUut1NhQGA==",
             "dev": true,
             "dependencies": {
                 "@babel/helper-validator-identifier": "^7.19.1",
-                "@eslint-community/eslint-utils": "^4.1.2",
-                "ci-info": "^3.6.1",
+                "@eslint-community/eslint-utils": "^4.4.0",
+                "ci-info": "^3.8.0",
                 "clean-regexp": "^1.0.0",
-                "esquery": "^1.4.0",
+                "esquery": "^1.5.0",
                 "indent-string": "^4.0.0",
-                "is-builtin-module": "^3.2.0",
+                "is-builtin-module": "^3.2.1",
                 "jsesc": "^3.0.2",
                 "lodash": "^4.17.21",
                 "pluralize": "^8.0.0",
                 "read-pkg-up": "^7.0.1",
                 "regexp-tree": "^0.1.24",
-                "regjsparser": "^0.9.1",
+                "regjsparser": "^0.10.0",
                 "safe-regex": "^2.1.1",
                 "semver": "^7.3.8",
                 "strip-indent": "^3.0.0"
             },
             "engines": {
-                "node": ">=14.18"
+                "node": ">=16"
             },
             "funding": {
                 "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
             },
             "peerDependencies": {
-                "eslint": ">=8.28.0"
+                "eslint": ">=8.38.0"
             }
         },
         "node_modules/eslint-plugin-unicorn/node_modules/ci-info": {
-            "version": "3.7.0",
-            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
-            "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+            "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
             "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/sibiraj-s"
+                }
+            ],
             "engines": {
                 "node": ">=8"
             }
@@ -3362,9 +3398,9 @@
             }
         },
         "node_modules/eslint-visitor-keys": {
-            "version": "3.4.0",
-            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
-            "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
+            "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
             "dev": true,
             "engines": {
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -3396,14 +3432,14 @@
             "dev": true
         },
         "node_modules/espree": {
-            "version": "9.5.1",
-            "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
-            "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+            "version": "9.5.2",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz",
+            "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==",
             "dev": true,
             "dependencies": {
                 "acorn": "^8.8.0",
                 "acorn-jsx": "^5.3.2",
-                "eslint-visitor-keys": "^3.4.0"
+                "eslint-visitor-keys": "^3.4.1"
             },
             "engines": {
                 "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -4112,12 +4148,13 @@
             }
         },
         "node_modules/get-intrinsic": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
-            "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+            "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
             "dependencies": {
                 "function-bind": "^1.1.1",
                 "has": "^1.0.3",
+                "has-proto": "^1.0.1",
                 "has-symbols": "^1.0.3"
             },
             "funding": {
@@ -4345,10 +4382,10 @@
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
             "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
         },
-        "node_modules/grapheme-splitter": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
-            "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+        "node_modules/graphemer": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+            "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
             "dev": true
         },
         "node_modules/handlebars": {
@@ -4440,6 +4477,17 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
+        "node_modules/has-proto": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+            "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/has-symbols": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
@@ -4768,12 +4816,11 @@
             }
         },
         "node_modules/internal-slot": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
-            "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
-            "dev": true,
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+            "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
             "dependencies": {
-                "get-intrinsic": "^1.1.0",
+                "get-intrinsic": "^1.2.0",
                 "has": "^1.0.3",
                 "side-channel": "^1.0.4"
             },
@@ -4805,6 +4852,19 @@
                 "url": "https://github.com/sponsors/ljharb"
             }
         },
+        "node_modules/is-array-buffer": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+            "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.2.0",
+                "is-typed-array": "^1.1.10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
         "node_modules/is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -4873,9 +4933,9 @@
             }
         },
         "node_modules/is-builtin-module": {
-            "version": "3.2.0",
-            "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
-            "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+            "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
             "dev": true,
             "dependencies": {
                 "builtin-modules": "^3.3.0"
@@ -5134,7 +5194,6 @@
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
             "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
-            "dev": true,
             "dependencies": {
                 "call-bind": "^1.0.2"
             },
@@ -5387,12 +5446,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/js-sdsl": {
-            "version": "4.1.5",
-            "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
-            "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
-            "dev": true
-        },
         "node_modules/js-tokens": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -5682,9 +5735,9 @@
             }
         },
         "node_modules/lint-staged": {
-            "version": "13.2.0",
-            "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.0.tgz",
-            "integrity": "sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==",
+            "version": "13.2.2",
+            "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz",
+            "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==",
             "dev": true,
             "dependencies": {
                 "chalk": "5.2.0",
@@ -5699,7 +5752,7 @@
                 "object-inspect": "^1.12.3",
                 "pidtree": "^0.6.0",
                 "string-argv": "^0.3.1",
-                "yaml": "^2.2.1"
+                "yaml": "^2.2.2"
             },
             "bin": {
                 "lint-staged": "bin/lint-staged.js"
@@ -8304,13 +8357,13 @@
             }
         },
         "node_modules/regexp.prototype.flags": {
-            "version": "1.4.3",
-            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
-            "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
+            "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
             "dependencies": {
                 "call-bind": "^1.0.2",
-                "define-properties": "^1.1.3",
-                "functions-have-names": "^1.2.2"
+                "define-properties": "^1.2.0",
+                "functions-have-names": "^1.2.3"
             },
             "engines": {
                 "node": ">= 0.4"
@@ -8342,9 +8395,9 @@
             }
         },
         "node_modules/regjsparser": {
-            "version": "0.9.1",
-            "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
-            "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+            "version": "0.10.0",
+            "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz",
+            "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==",
             "dev": true,
             "dependencies": {
                 "jsesc": "~0.5.0"
@@ -8666,9 +8719,9 @@
             "dev": true
         },
         "node_modules/sfmc-sdk": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/sfmc-sdk/-/sfmc-sdk-1.0.0.tgz",
-            "integrity": "sha512-Okw99BwTXYsTxrt1URYBiWvC8xFGZSn1bB+uO/Z2i/tn/f2CxzsJusrCGmq8IlX/oXJXaYsI1kSulbUrcOtDtg==",
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/sfmc-sdk/-/sfmc-sdk-1.0.1.tgz",
+            "integrity": "sha512-0GdDnNa6jqoLGSHf3ByoQNAzlzdGxjSHeH55zFaY9tM4YPtSRgTrsBkZCh5xOBLwr4u7u2X9lbJpxbTWyULhpA==",
             "dependencies": {
                 "axios": "^1.3.5",
                 "fast-xml-parser": "4.2.0",
@@ -8740,9 +8793,9 @@
             "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
         },
         "node_modules/simple-git": {
-            "version": "3.16.0",
-            "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.16.0.tgz",
-            "integrity": "sha512-zuWYsOLEhbJRWVxpjdiXl6eyAyGo/KzVW+KFhhw9MqEEJttcq+32jTWSGyxTdf9e/YCohxRE+9xpWFj9FdiJNw==",
+            "version": "3.18.0",
+            "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.18.0.tgz",
+            "integrity": "sha512-Yt0GJ5aYrpPci3JyrYcsPz8Xc05Hi4JPSOb+Sgn/BmPX35fn/6Fp9Mef8eMBCrL2siY5w4j49TA5Q+bxPpri1Q==",
             "dependencies": {
                 "@kwsites/file-exists": "^1.1.1",
                 "@kwsites/promise-deferred": "^1.1.1",
@@ -8939,6 +8992,17 @@
             "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
             "dev": true
         },
+        "node_modules/stop-iteration-iterator": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+            "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+            "dependencies": {
+                "internal-slot": "^1.0.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
         "node_modules/stream-connect": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz",
@@ -9847,9 +9911,9 @@
             }
         },
         "node_modules/yargs": {
-            "version": "17.6.0",
-            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
-            "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
+            "version": "17.7.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
             "dependencies": {
                 "cliui": "^8.0.1",
                 "escalade": "^3.1.1",
@@ -9857,16 +9921,16 @@
                 "require-directory": "^2.1.1",
                 "string-width": "^4.2.3",
                 "y18n": "^5.0.5",
-                "yargs-parser": "^21.0.0"
+                "yargs-parser": "^21.1.1"
             },
             "engines": {
                 "node": ">=12"
             }
         },
         "node_modules/yargs-parser": {
-            "version": "21.0.1",
-            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
-            "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
+            "version": "21.1.1",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+            "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
             "engines": {
                 "node": ">=12"
             }
@@ -10268,9 +10332,9 @@
             }
         },
         "@es-joy/jsdoccomment": {
-            "version": "0.37.1",
-            "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.37.1.tgz",
-            "integrity": "sha512-5vxWJ1gEkEF0yRd0O+uK6dHJf7adrxwQSX8PuRiPfFSAbNLnY0ZJfXaZucoz14Jj2N11xn2DnlEPwWRpYpvRjg==",
+            "version": "0.39.4",
+            "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz",
+            "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==",
             "dev": true,
             "requires": {
                 "comment-parser": "1.3.1",
@@ -10279,9 +10343,9 @@
             }
         },
         "@eslint-community/eslint-utils": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz",
-            "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==",
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+            "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
             "dev": true,
             "requires": {
                 "eslint-visitor-keys": "^3.3.0"
@@ -10294,14 +10358,14 @@
             "dev": true
         },
         "@eslint/eslintrc": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
-            "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz",
+            "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==",
             "dev": true,
             "requires": {
                 "ajv": "^6.12.4",
                 "debug": "^4.3.2",
-                "espree": "^9.5.1",
+                "espree": "^9.5.2",
                 "globals": "^13.19.0",
                 "ignore": "^5.2.0",
                 "import-fresh": "^3.2.1",
@@ -10331,9 +10395,9 @@
             }
         },
         "@eslint/js": {
-            "version": "8.39.0",
-            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
-            "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
+            "version": "8.41.0",
+            "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
+            "integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
             "dev": true
         },
         "@humanwhocodes/config-array": {
@@ -10903,6 +10967,15 @@
             "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==",
             "dev": true
         },
+        "array-buffer-byte-length": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
+            "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "is-array-buffer": "^3.0.1"
+            }
+        },
         "array-differ": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz",
@@ -11870,25 +11943,28 @@
             }
         },
         "deep-equal": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.1.0.tgz",
-            "integrity": "sha512-2pxgvWu3Alv1PoWEyVg7HS8YhGlUFUV7N5oOvfL6d+7xAmLSemMwv/c8Zv/i9KFzxV5Kt5CAvQc70fLwVuf4UA==",
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz",
+            "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==",
             "requires": {
+                "array-buffer-byte-length": "^1.0.0",
                 "call-bind": "^1.0.2",
-                "es-get-iterator": "^1.1.2",
-                "get-intrinsic": "^1.1.3",
+                "es-get-iterator": "^1.1.3",
+                "get-intrinsic": "^1.2.0",
                 "is-arguments": "^1.1.1",
+                "is-array-buffer": "^3.0.2",
                 "is-date-object": "^1.0.5",
                 "is-regex": "^1.1.4",
+                "is-shared-array-buffer": "^1.0.2",
                 "isarray": "^2.0.5",
                 "object-is": "^1.1.5",
                 "object-keys": "^1.1.1",
                 "object.assign": "^4.1.4",
-                "regexp.prototype.flags": "^1.4.3",
+                "regexp.prototype.flags": "^1.5.0",
                 "side-channel": "^1.0.4",
                 "which-boxed-primitive": "^1.0.2",
                 "which-collection": "^1.0.1",
-                "which-typed-array": "^1.1.8"
+                "which-typed-array": "^1.1.9"
             }
         },
         "deep-extend": {
@@ -11933,9 +12009,9 @@
             "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
         },
         "define-properties": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
-            "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
+            "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==",
             "requires": {
                 "has-property-descriptors": "^1.0.0",
                 "object-keys": "^1.1.1"
@@ -12203,18 +12279,19 @@
             }
         },
         "es-get-iterator": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
-            "integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
+            "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
             "requires": {
                 "call-bind": "^1.0.2",
-                "get-intrinsic": "^1.1.0",
-                "has-symbols": "^1.0.1",
-                "is-arguments": "^1.1.0",
+                "get-intrinsic": "^1.1.3",
+                "has-symbols": "^1.0.3",
+                "is-arguments": "^1.1.1",
                 "is-map": "^2.0.2",
                 "is-set": "^2.0.2",
-                "is-string": "^1.0.5",
-                "isarray": "^2.0.5"
+                "is-string": "^1.0.7",
+                "isarray": "^2.0.5",
+                "stop-iteration-iterator": "^1.0.0"
             }
         },
         "es-to-primitive": {
@@ -12257,15 +12334,15 @@
             "dev": true
         },
         "eslint": {
-            "version": "8.39.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
-            "integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
+            "version": "8.41.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
+            "integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
             "dev": true,
             "requires": {
                 "@eslint-community/eslint-utils": "^4.2.0",
                 "@eslint-community/regexpp": "^4.4.0",
-                "@eslint/eslintrc": "^2.0.2",
-                "@eslint/js": "8.39.0",
+                "@eslint/eslintrc": "^2.0.3",
+                "@eslint/js": "8.41.0",
                 "@humanwhocodes/config-array": "^0.11.8",
                 "@humanwhocodes/module-importer": "^1.0.1",
                 "@nodelib/fs.walk": "^1.2.8",
@@ -12276,8 +12353,8 @@
                 "doctrine": "^3.0.0",
                 "escape-string-regexp": "^4.0.0",
                 "eslint-scope": "^7.2.0",
-                "eslint-visitor-keys": "^3.4.0",
-                "espree": "^9.5.1",
+                "eslint-visitor-keys": "^3.4.1",
+                "espree": "^9.5.2",
                 "esquery": "^1.4.2",
                 "esutils": "^2.0.2",
                 "fast-deep-equal": "^3.1.3",
@@ -12285,13 +12362,12 @@
                 "find-up": "^5.0.0",
                 "glob-parent": "^6.0.2",
                 "globals": "^13.19.0",
-                "grapheme-splitter": "^1.0.4",
+                "graphemer": "^1.4.0",
                 "ignore": "^5.2.0",
                 "import-fresh": "^3.0.0",
                 "imurmurhash": "^0.1.4",
                 "is-glob": "^4.0.0",
                 "is-path-inside": "^3.0.3",
-                "js-sdsl": "^4.1.4",
                 "js-yaml": "^4.1.0",
                 "json-stable-stringify-without-jsonify": "^1.0.1",
                 "levn": "^0.4.1",
@@ -12339,19 +12415,30 @@
             "requires": {}
         },
         "eslint-plugin-jsdoc": {
-            "version": "43.1.1",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-43.1.1.tgz",
-            "integrity": "sha512-J2kjjsJ5vBXSyNzqJhceeSGTAgVgZHcPSJKo3vD4tNjUdfky98rR2VfZUDsS1GKL6isyVa8GWvr+Az7Vyg2HXA==",
+            "version": "45.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz",
+            "integrity": "sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g==",
             "dev": true,
             "requires": {
-                "@es-joy/jsdoccomment": "~0.37.1",
+                "@es-joy/jsdoccomment": "~0.39.4",
                 "are-docs-informative": "^0.0.2",
                 "comment-parser": "1.3.1",
                 "debug": "^4.3.4",
                 "escape-string-regexp": "^4.0.0",
                 "esquery": "^1.5.0",
-                "semver": "^7.5.0",
+                "semver": "^7.5.1",
                 "spdx-expression-parse": "^3.0.1"
+            },
+            "dependencies": {
+                "semver": {
+                    "version": "7.5.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
+                    "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+                    "dev": true,
+                    "requires": {
+                        "lru-cache": "^6.0.0"
+                    }
+                }
             }
         },
         "eslint-plugin-mocha": {
@@ -12374,33 +12461,33 @@
             }
         },
         "eslint-plugin-unicorn": {
-            "version": "46.0.0",
-            "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-46.0.0.tgz",
-            "integrity": "sha512-j07WkC+PFZwk8J33LYp6JMoHa1lXc1u6R45pbSAipjpfpb7KIGr17VE2D685zCxR5VL4cjrl65kTJflziQWMDA==",
+            "version": "47.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-47.0.0.tgz",
+            "integrity": "sha512-ivB3bKk7fDIeWOUmmMm9o3Ax9zbMz1Bsza/R2qm46ufw4T6VBFBaJIR1uN3pCKSmSXm8/9Nri8V+iUut1NhQGA==",
             "dev": true,
             "requires": {
                 "@babel/helper-validator-identifier": "^7.19.1",
-                "@eslint-community/eslint-utils": "^4.1.2",
-                "ci-info": "^3.6.1",
+                "@eslint-community/eslint-utils": "^4.4.0",
+                "ci-info": "^3.8.0",
                 "clean-regexp": "^1.0.0",
-                "esquery": "^1.4.0",
+                "esquery": "^1.5.0",
                 "indent-string": "^4.0.0",
-                "is-builtin-module": "^3.2.0",
+                "is-builtin-module": "^3.2.1",
                 "jsesc": "^3.0.2",
                 "lodash": "^4.17.21",
                 "pluralize": "^8.0.0",
                 "read-pkg-up": "^7.0.1",
                 "regexp-tree": "^0.1.24",
-                "regjsparser": "^0.9.1",
+                "regjsparser": "^0.10.0",
                 "safe-regex": "^2.1.1",
                 "semver": "^7.3.8",
                 "strip-indent": "^3.0.0"
             },
             "dependencies": {
                 "ci-info": {
-                    "version": "3.7.0",
-                    "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz",
-                    "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==",
+                    "version": "3.8.0",
+                    "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+                    "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
                     "dev": true
                 },
                 "jsesc": {
@@ -12439,20 +12526,20 @@
             }
         },
         "eslint-visitor-keys": {
-            "version": "3.4.0",
-            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
-            "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
+            "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
             "dev": true
         },
         "espree": {
-            "version": "9.5.1",
-            "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
-            "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+            "version": "9.5.2",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz",
+            "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==",
             "dev": true,
             "requires": {
                 "acorn": "^8.8.0",
                 "acorn-jsx": "^5.3.2",
-                "eslint-visitor-keys": "^3.4.0"
+                "eslint-visitor-keys": "^3.4.1"
             }
         },
         "esprima": {
@@ -12954,12 +13041,13 @@
             "dev": true
         },
         "get-intrinsic": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
-            "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
+            "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
             "requires": {
                 "function-bind": "^1.1.1",
                 "has": "^1.0.3",
+                "has-proto": "^1.0.1",
                 "has-symbols": "^1.0.3"
             }
         },
@@ -13124,10 +13212,10 @@
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
             "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
         },
-        "grapheme-splitter": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
-            "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+        "graphemer": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+            "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
             "dev": true
         },
         "handlebars": {
@@ -13192,6 +13280,11 @@
                 "get-intrinsic": "^1.1.1"
             }
         },
+        "has-proto": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+            "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg=="
+        },
         "has-symbols": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
@@ -13432,12 +13525,11 @@
             }
         },
         "internal-slot": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
-            "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
-            "dev": true,
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
+            "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==",
             "requires": {
-                "get-intrinsic": "^1.1.0",
+                "get-intrinsic": "^1.2.0",
                 "has": "^1.0.3",
                 "side-channel": "^1.0.4"
             }
@@ -13460,6 +13552,16 @@
                 "has-tostringtag": "^1.0.0"
             }
         },
+        "is-array-buffer": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
+            "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.2.0",
+                "is-typed-array": "^1.1.10"
+            }
+        },
         "is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -13499,9 +13601,9 @@
             "dev": true
         },
         "is-builtin-module": {
-            "version": "3.2.0",
-            "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
-            "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+            "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
             "dev": true,
             "requires": {
                 "builtin-modules": "^3.3.0"
@@ -13661,7 +13763,6 @@
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
             "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
-            "dev": true,
             "requires": {
                 "call-bind": "^1.0.2"
             }
@@ -13846,12 +13947,6 @@
                 "istanbul-lib-report": "^3.0.0"
             }
         },
-        "js-sdsl": {
-            "version": "4.1.5",
-            "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
-            "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
-            "dev": true
-        },
         "js-tokens": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -14090,9 +14185,9 @@
             }
         },
         "lint-staged": {
-            "version": "13.2.0",
-            "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.0.tgz",
-            "integrity": "sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==",
+            "version": "13.2.2",
+            "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz",
+            "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==",
             "dev": true,
             "requires": {
                 "chalk": "5.2.0",
@@ -14107,7 +14202,7 @@
                 "object-inspect": "^1.12.3",
                 "pidtree": "^0.6.0",
                 "string-argv": "^0.3.1",
-                "yaml": "^2.2.1"
+                "yaml": "^2.2.2"
             },
             "dependencies": {
                 "chalk": {
@@ -16074,13 +16169,13 @@
             "dev": true
         },
         "regexp.prototype.flags": {
-            "version": "1.4.3",
-            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
-            "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz",
+            "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==",
             "requires": {
                 "call-bind": "^1.0.2",
-                "define-properties": "^1.1.3",
-                "functions-have-names": "^1.2.2"
+                "define-properties": "^1.2.0",
+                "functions-have-names": "^1.2.3"
             }
         },
         "registry-auth-token": {
@@ -16100,9 +16195,9 @@
             }
         },
         "regjsparser": {
-            "version": "0.9.1",
-            "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
-            "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==",
+            "version": "0.10.0",
+            "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz",
+            "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==",
             "dev": true,
             "requires": {
                 "jsesc": "~0.5.0"
@@ -16333,9 +16428,9 @@
             "dev": true
         },
         "sfmc-sdk": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/sfmc-sdk/-/sfmc-sdk-1.0.0.tgz",
-            "integrity": "sha512-Okw99BwTXYsTxrt1URYBiWvC8xFGZSn1bB+uO/Z2i/tn/f2CxzsJusrCGmq8IlX/oXJXaYsI1kSulbUrcOtDtg==",
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/sfmc-sdk/-/sfmc-sdk-1.0.1.tgz",
+            "integrity": "sha512-0GdDnNa6jqoLGSHf3ByoQNAzlzdGxjSHeH55zFaY9tM4YPtSRgTrsBkZCh5xOBLwr4u7u2X9lbJpxbTWyULhpA==",
             "requires": {
                 "axios": "^1.3.5",
                 "fast-xml-parser": "4.2.0",
@@ -16389,9 +16484,9 @@
             "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
         },
         "simple-git": {
-            "version": "3.16.0",
-            "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.16.0.tgz",
-            "integrity": "sha512-zuWYsOLEhbJRWVxpjdiXl6eyAyGo/KzVW+KFhhw9MqEEJttcq+32jTWSGyxTdf9e/YCohxRE+9xpWFj9FdiJNw==",
+            "version": "3.18.0",
+            "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.18.0.tgz",
+            "integrity": "sha512-Yt0GJ5aYrpPci3JyrYcsPz8Xc05Hi4JPSOb+Sgn/BmPX35fn/6Fp9Mef8eMBCrL2siY5w4j49TA5Q+bxPpri1Q==",
             "requires": {
                 "@kwsites/file-exists": "^1.1.1",
                 "@kwsites/promise-deferred": "^1.1.1",
@@ -16551,6 +16646,14 @@
             "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
             "dev": true
         },
+        "stop-iteration-iterator": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
+            "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
+            "requires": {
+                "internal-slot": "^1.0.4"
+            }
+        },
         "stream-connect": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz",
@@ -17243,9 +17346,9 @@
             "dev": true
         },
         "yargs": {
-            "version": "17.6.0",
-            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
-            "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
+            "version": "17.7.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+            "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
             "requires": {
                 "cliui": "^8.0.1",
                 "escalade": "^3.1.1",
@@ -17253,7 +17356,7 @@
                 "require-directory": "^2.1.1",
                 "string-width": "^4.2.3",
                 "y18n": "^5.0.5",
-                "yargs-parser": "^21.0.0"
+                "yargs-parser": "^21.1.1"
             },
             "dependencies": {
                 "cliui": {
@@ -17269,9 +17372,9 @@
             }
         },
         "yargs-parser": {
-            "version": "21.0.1",
-            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
-            "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg=="
+            "version": "21.1.1",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+            "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
         },
         "yargs-unparser": {
             "version": "2.0.0",
diff --git a/package.json b/package.json
index fbfe1fef..255dd321 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "mcdev",
-    "version": "5.0.1",
+    "version": "5.0.2",
     "description": "Accenture Salesforce Marketing Cloud DevTools",
     "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
     "license": "MIT",
@@ -63,7 +63,7 @@
         "command-exists": "1.2.9",
         "conf": "10.2.0",
         "console.table": "0.10.0",
-        "deep-equal": "2.1.0",
+        "deep-equal": "2.2.1",
         "fs-extra": "11.1.0",
         "inquirer": "8.2.5",
         "json-to-table": "4.2.1",
@@ -72,29 +72,29 @@
         "prettier": "2.8.8",
         "prettier-plugin-sql": "0.14.0",
         "semver": "7.5.0",
-        "sfmc-sdk": "1.0.0",
-        "simple-git": "3.16.0",
+        "sfmc-sdk": "1.0.1",
+        "simple-git": "3.18.0",
         "toposort": "2.0.2",
         "update-notifier": "5.1.0",
         "winston": "3.8.2",
-        "yargs": "17.6.0"
+        "yargs": "17.7.2"
     },
     "devDependencies": {
         "assert": "2.0.0",
         "axios-mock-adapter": "1.21.3",
         "chai": "4.3.7",
         "chai-files": "1.4.0",
-        "eslint": "8.39.0",
+        "eslint": "8.41.0",
         "eslint-config-prettier": "8.7.0",
         "eslint-config-ssjs": "1.1.11",
-        "eslint-plugin-jsdoc": "43.1.1",
+        "eslint-plugin-jsdoc": "45.0.0",
         "eslint-plugin-mocha": "10.1.0",
         "eslint-plugin-prettier": "4.2.1",
-        "eslint-plugin-unicorn": "46.0.0",
+        "eslint-plugin-unicorn": "47.0.0",
         "fast-xml-parser": "4.2.2",
         "husky": "8.0.3",
         "jsdoc-to-markdown": "8.0.0",
-        "lint-staged": "13.2.0",
+        "lint-staged": "13.2.2",
         "mocha": "10.2.0",
         "mock-fs": "5.2.0",
         "npm-check": "6.0.1",


Test patch:
diff --git a/test/mockRoot/.mcdevrc.json b/test/mockRoot/.mcdevrc.json
index f4763aee..f8e5c389 100644
--- a/test/mockRoot/.mcdevrc.json
+++ b/test/mockRoot/.mcdevrc.json
@@ -41,6 +41,7 @@
             "sharedFolder": "/Shared Data Extensions/test",
             "suffix": "_test",
             "prefix": "testExisting_",
+            "prefixUpper": "TESTEXISTING_",
             "description": "bla bla",
             "countryCodeIn": "'test'"
         },
@@ -51,6 +52,7 @@
             "sharedFolder": "/Shared Data Extensions/test target",
             "suffix": "_testTarget",
             "prefix": "testTemplated_",
+            "prefixUpper": "TESTTEMPLATED_",
             "description": "foobar",
             "countryCodeIn": "'testTarget'"
         }
@@ -76,5 +78,5 @@
             "triggeredSend"
         ]
     },
-    "version": "5.0.1"
+    "version": "5.0.2"
 }
diff --git a/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.amp b/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.amp
similarity index 100%
rename from test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.amp
rename to test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.amp
diff --git a/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.json b/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.json
similarity index 55%
rename from test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.json
rename to test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.json
index 0e352532..ee2208c0 100644
--- a/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.json
+++ b/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.json
@@ -1,8 +1,5 @@
 {
-    "code": {
-        "code": "4912312345678"
-    },
-    "keyword": "testNew_keyword",
+    "c__codeKeyword": "4912312345678.TESTNEW_KEYWORD",
     "status": "Active",
     "restriction": "NONE",
     "keywordType": "STOP",
diff --git a/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.amp b/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.amp
similarity index 100%
rename from test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.amp
rename to test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.amp
diff --git a/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.json b/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.json
similarity index 53%
rename from test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.json
rename to test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.json
index ba94f539..69e9a88a 100644
--- a/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.json
+++ b/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.json
@@ -1,8 +1,5 @@
 {
-    "code": {
-        "code": "4912312345678"
-    },
-    "keyword": "testNew_keyword_blocked",
+    "c__codeKeyword": "4912312345678.TESTNEW_KEYWORD_BLOCKED",
     "status": "Active",
     "restriction": "NONE",
     "keywordType": "NORMAL",
diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json
index 4cf9c739..3ec2688e 100644
--- a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json
+++ b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json
@@ -10,6 +10,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json
index cb2fc540..262b7600 100644
--- a/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json
+++ b/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json
@@ -10,6 +10,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/1111111/user/retrieve-expected.md b/test/resources/1111111/user/retrieve-expected.md
new file mode 100644
index 00000000..50eb1b2e
--- /dev/null
+++ b/test/resources/1111111/user/retrieve-expected.md
@@ -0,0 +1,19 @@
+# User Overview - testInstance
+
+## Users (1)
+
+| Name | Last successful Login | Active | Access Locked out | API User | Must change PW | Default BU | BU Access | Roles | Login | ID | Key | E-Mail | Notification E-Mail | Timezone | SFMC Locale | Modified Date | Modified By | Created Date |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| user test | 96 days | ✓ | - | - | - | <nobr>_ParentBU_ (1111111)</nobr> | <nobr>_ParentBU_ (1111111)</nobr>,<br> <nobr>testBU (9999999)</nobr> | <nobr>Administrator,</nobr><br> <nobr>Content Creator,</nobr><br> <nobr>Marketing Cloud Administrator</nobr> | user_test@accenture.asgr | 700301950 | testExisting_user | user_test@accenture.com | user_test@accenture.com | GMT+01:00 | en-GB | 2022-06-21 01:43:02.64 | 123456 | 2019-09-06 01:59:07.097 |
+
+
+## Inactivated Users (0)
+
+| Name | Last successful Login | Active | Access Locked out | API User | Must change PW | Default BU | BU Access | Roles | Login | ID | Key | E-Mail | Notification E-Mail | Timezone | SFMC Locale | Modified Date | Modified By | Created Date |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+
+
+## Installed Packages (0)
+
+| Name | Last successful Login | Active | Access Locked out | API User | Must change PW | Default BU | BU Access | Roles | Login | ID | Key | E-Mail | Notification E-Mail | Timezone | SFMC Locale | Modified Date | Modified By | Created Date |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
diff --git a/test/resources/9999999/dataExtension/retrieve-expected.md b/test/resources/9999999/dataExtension/retrieve-expected.md
new file mode 100644
index 00000000..fda321a2
--- /dev/null
+++ b/test/resources/9999999/dataExtension/retrieve-expected.md
@@ -0,0 +1,18 @@
+## testExisting_dataExtension
+
+**Description:** bla bla
+
+**Folder:** Data Extensions/
+
+**Fields in table:** 4
+
+**Sendable:** Yes (`ContactKey` to `Subscriber Key`)
+
+**Testable:** Yes
+
+| Name | FieldType | MaxLength | IsPrimaryKey | IsNullable | DefaultValue |
+| --- | --- | --- | --- | --- | --- |
+| FirstName | Text | 50 | - | + |  |
+| LastName | Text | 50 | - | + |  |
+| EmailAddress | EmailAddress | 254 | - | - |  |
+| ContactKey | Text | 50 | + | - |  |
diff --git a/test/resources/9999999/journey/build-expected.json b/test/resources/9999999/journey/build-expected.json
index 87036f2b..b3e4d548 100644
--- a/test/resources/9999999/journey/build-expected.json
+++ b/test/resources/9999999/journey/build-expected.json
@@ -323,7 +323,7 @@
             },
             "configurationArguments": {
                 "c__mobileMessage_id": "NTIzOjc4OjA",
-                "c__mobileKeyword": "testTemplated_keyword",
+                "r__mobileKeyword_codeKeyword": "4912312345678.TESTTEMPLATED_KEYWORD",
                 "isOptIn": false,
                 "honorBlackoutWindowEnum": 0,
                 "mobileBlackoutWindowStartTime": "",
diff --git a/test/resources/9999999/journey/get-expected.json b/test/resources/9999999/journey/get-expected.json
index ba60a360..19b1f933 100644
--- a/test/resources/9999999/journey/get-expected.json
+++ b/test/resources/9999999/journey/get-expected.json
@@ -326,7 +326,7 @@
             },
             "configurationArguments": {
                 "c__mobileMessage_id": "NTIzOjc4OjA",
-                "c__mobileKeyword": "testExisting_keyword",
+                "r__mobileKeyword_codeKeyword": "4912312345678.TESTEXISTING_KEYWORD",
                 "isOptIn": false,
                 "honorBlackoutWindowEnum": 0,
                 "mobileBlackoutWindowStartTime": "",
diff --git a/test/resources/9999999/journey/put-expected.json b/test/resources/9999999/journey/put-expected.json
index f21faf54..aa005cf6 100644
--- a/test/resources/9999999/journey/put-expected.json
+++ b/test/resources/9999999/journey/put-expected.json
@@ -326,7 +326,7 @@
             },
             "configurationArguments": {
                 "c__mobileMessage_id": "NTIzOjc4OjA",
-                "c__mobileKeyword": "testExisting_keyword",
+                "r__mobileKeyword_codeKeyword": "4912312345678.TESTEXISTING_KEYWORD",
                 "isOptIn": false,
                 "honorBlackoutWindowEnum": 0,
                 "mobileBlackoutWindowStartTime": "",
diff --git a/test/resources/9999999/journey/template-expected.json b/test/resources/9999999/journey/template-expected.json
index c474ee3e..ec0b7e58 100644
--- a/test/resources/9999999/journey/template-expected.json
+++ b/test/resources/9999999/journey/template-expected.json
@@ -323,7 +323,7 @@
             },
             "configurationArguments": {
                 "c__mobileMessage_id": "NTIzOjc4OjA",
-                "c__mobileKeyword": "{{{prefix}}}keyword",
+                "r__mobileKeyword_codeKeyword": "4912312345678.{{{prefixUpper}}}KEYWORD",
                 "isOptIn": false,
                 "honorBlackoutWindowEnum": 0,
                 "mobileBlackoutWindowStartTime": "",
diff --git a/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json b/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json
index c58232ce..cde6ad4a 100644
--- a/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json
+++ b/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json
@@ -25,7 +25,7 @@
         "isClientOwned": false,
         "isOwner": false
     },
-    "keyword": "testNew_keyword",
+    "keyword": "TESTNEW_KEYWORD",
     "startDate": "2023-03-30T14:00:11.843Z",
     "endDate": "2043-03-30T14:00:11.843Z",
     "status": "Active",
diff --git a/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json b/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json
index 95978299..b256ebe2 100644
--- a/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json
+++ b/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json
@@ -32,7 +32,7 @@
                 "isClientOwned": false,
                 "isOwner": false
             },
-            "keyword": "testExisting_keyword",
+            "keyword": "TESTEXISTING_KEYWORD",
             "startDate": "2018-03-27T12:37:18.197Z",
             "endDate": "2038-03-27T12:37:18.197Z",
             "status": "Active",
diff --git a/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json b/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json
index 22a60470..5e69ccee 100644
--- a/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json
+++ b/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json
@@ -90,7 +90,7 @@
     "isSentImmediately": true,
     "nextKeyword": {
         "id": "cTVJaG5oSDJPVUNHcUh6Z3pQT2tVdzo4Njow",
-        "keyword": "testExisting_keyword",
+        "keyword": "TESTEXISTING_KEYWORD",
         "restriction": "NONE",
         "isInherited": false
     },
diff --git a/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json b/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json
index 43ba203a..8cf07883 100644
--- a/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json
+++ b/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json
@@ -111,7 +111,7 @@
             "isSentImmediately": true,
             "nextKeyword": {
                 "id": "cTVJaG5oSDJPVUNHcUh6Z3pQT2tVdzo4Njow",
-                "keyword": "testExisting_keyword",
+                "keyword": "TESTEXISTING_KEYWORD",
                 "restriction": "NONE",
                 "isInherited": false
             },
diff --git a/test/resources/9999999/messaging/v1/sms/definitions/post-response.json b/test/resources/9999999/messaging/v1/sms/definitions/post-response.json
index c66b9bda..d78803aa 100644
--- a/test/resources/9999999/messaging/v1/sms/definitions/post-response.json
+++ b/test/resources/9999999/messaging/v1/sms/definitions/post-response.json
@@ -12,6 +12,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json
index cfca5868..e42c625f 100644
--- a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json
+++ b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json
@@ -13,6 +13,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json
index cfca5868..e42c625f 100644
--- a/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json
+++ b/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json
@@ -13,6 +13,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/mobileKeyword/build-expected.json b/test/resources/9999999/mobileKeyword/build-expected.json
index 75331b6b..d23e6a7c 100644
--- a/test/resources/9999999/mobileKeyword/build-expected.json
+++ b/test/resources/9999999/mobileKeyword/build-expected.json
@@ -1,8 +1,5 @@
 {
-    "code": {
-        "code": "4912312345678"
-    },
-    "keyword": "testTemplated_keyword",
+    "c__codeKeyword": "4912312345678.TESTTEMPLATED_KEYWORD",
     "keywordType": "NORMAL",
     "isInherited": false,
     "restriction": "NONE"
diff --git a/test/resources/9999999/mobileKeyword/get-expected.json b/test/resources/9999999/mobileKeyword/get-expected.json
index 398d759c..f06aecbe 100644
--- a/test/resources/9999999/mobileKeyword/get-expected.json
+++ b/test/resources/9999999/mobileKeyword/get-expected.json
@@ -1,10 +1,7 @@
 {
     "createdDate": "2018-03-27T12:37:18.12Z",
     "lastUpdated": "2018-03-27T12:37:18.167Z",
-    "code": {
-        "code": "4912312345678"
-    },
-    "keyword": "testExisting_keyword",
+    "c__codeKeyword": "4912312345678.TESTEXISTING_KEYWORD",
     "startDate": "2018-03-27T12:37:18.197Z",
     "endDate": "2038-03-27T12:37:18.197Z",
     "status": "Active",
diff --git a/test/resources/9999999/mobileKeyword/post-create-expected.json b/test/resources/9999999/mobileKeyword/post-create-expected.json
index 0c3f1b7d..bfca2dd7 100644
--- a/test/resources/9999999/mobileKeyword/post-create-expected.json
+++ b/test/resources/9999999/mobileKeyword/post-create-expected.json
@@ -1,14 +1,11 @@
 {
-    "code": {
-        "code": "4912312345678"
-    },
     "createdBy": {
         "name": "API_JornBerkefeld app user"
     },
     "createdDate": "2023-03-30T14:00:11.907Z",
     "endDate": "2043-03-30T14:00:11.843Z",
     "isInherited": false,
-    "keyword": "testNew_keyword",
+    "c__codeKeyword": "4912312345678.TESTNEW_KEYWORD",
     "keywordType": "NORMAL",
     "lastUpdated": "2023-03-30T14:00:12.295Z",
     "restriction": "NONE",
diff --git a/test/resources/9999999/mobileKeyword/template-expected.json b/test/resources/9999999/mobileKeyword/template-expected.json
index a79e3f45..d14dac5f 100644
--- a/test/resources/9999999/mobileKeyword/template-expected.json
+++ b/test/resources/9999999/mobileKeyword/template-expected.json
@@ -1,8 +1,5 @@
 {
-    "code": {
-        "code": "4912312345678"
-    },
-    "keyword": "{{{prefix}}}keyword",
+    "c__codeKeyword": "4912312345678.{{{prefixUpper}}}KEYWORD",
     "keywordType": "NORMAL",
     "isInherited": false,
     "restriction": "NONE"
diff --git a/test/resources/9999999/query/build-expected.sql b/test/resources/9999999/query/build-expected.sql
index 276ccd7a..4385e76e 100644
--- a/test/resources/9999999/query/build-expected.sql
+++ b/test/resources/9999999/query/build-expected.sql
@@ -1,5 +1,5 @@
 SELECT
-    SubscriberKey as testField
+    SubscriberKey AS testField
 FROM
     _Subscribers
 WHERE
diff --git a/test/resources/9999999/query/get-expected.sql b/test/resources/9999999/query/get-expected.sql
index e7852540..2a32f5fa 100644
--- a/test/resources/9999999/query/get-expected.sql
+++ b/test/resources/9999999/query/get-expected.sql
@@ -1,5 +1,5 @@
 SELECT
-    SubscriberKey as testField
+    SubscriberKey AS testField
 FROM
     _Subscribers
 WHERE
diff --git a/test/resources/9999999/query/patch-expected.sql b/test/resources/9999999/query/patch-expected.sql
index e7852540..2a32f5fa 100644
--- a/test/resources/9999999/query/patch-expected.sql
+++ b/test/resources/9999999/query/patch-expected.sql
@@ -1,5 +1,5 @@
 SELECT
-    SubscriberKey as testField
+    SubscriberKey AS testField
 FROM
     _Subscribers
 WHERE
diff --git a/test/resources/9999999/query/post-expected.sql b/test/resources/9999999/query/post-expected.sql
index 8020314e..4148833c 100644
--- a/test/resources/9999999/query/post-expected.sql
+++ b/test/resources/9999999/query/post-expected.sql
@@ -1,4 +1,4 @@
 SELECT
-    SubscriberKey as testField
+    SubscriberKey AS testField
 FROM
     _Subscribers
diff --git a/test/resources/9999999/query/template-expected.sql b/test/resources/9999999/query/template-expected.sql
index 0ed1dc1c..06aae9b3 100644
--- a/test/resources/9999999/query/template-expected.sql
+++ b/test/resources/9999999/query/template-expected.sql
@@ -1,5 +1,5 @@
 SELECT
-    SubscriberKey as testField
+    SubscriberKey AS testField
 FROM
     _Subscribers
 WHERE
diff --git a/test/resources/9999999/transactionalSMS/build-expected.json b/test/resources/9999999/transactionalSMS/build-expected.json
index 43f5c142..951d2e44 100644
--- a/test/resources/9999999/transactionalSMS/build-expected.json
+++ b/test/resources/9999999/transactionalSMS/build-expected.json
@@ -8,6 +8,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testTemplated_keyword"
+        "keyword": "TESTTEMPLATED_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/transactionalSMS/get-expected.json b/test/resources/9999999/transactionalSMS/get-expected.json
index 4cf9c739..3ec2688e 100644
--- a/test/resources/9999999/transactionalSMS/get-expected.json
+++ b/test/resources/9999999/transactionalSMS/get-expected.json
@@ -10,6 +10,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/transactionalSMS/patch-expected.json b/test/resources/9999999/transactionalSMS/patch-expected.json
index 4cf9c739..3ec2688e 100644
--- a/test/resources/9999999/transactionalSMS/patch-expected.json
+++ b/test/resources/9999999/transactionalSMS/patch-expected.json
@@ -10,6 +10,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/transactionalSMS/post-expected.json b/test/resources/9999999/transactionalSMS/post-expected.json
index cb2fc540..262b7600 100644
--- a/test/resources/9999999/transactionalSMS/post-expected.json
+++ b/test/resources/9999999/transactionalSMS/post-expected.json
@@ -10,6 +10,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "testExisting_keyword"
+        "keyword": "TESTEXISTING_KEYWORD"
     }
 }
diff --git a/test/resources/9999999/transactionalSMS/template-expected.json b/test/resources/9999999/transactionalSMS/template-expected.json
index 238056a5..302c2067 100644
--- a/test/resources/9999999/transactionalSMS/template-expected.json
+++ b/test/resources/9999999/transactionalSMS/template-expected.json
@@ -8,6 +8,6 @@
         "countryCode": "",
         "autoAddSubscriber": true,
         "updateSubscriber": true,
-        "keyword": "{{{prefix}}}keyword"
+        "keyword": "{{{prefixUpper}}}KEYWORD"
     }
 }
diff --git a/test/type.dataExtension.test.js b/test/type.dataExtension.test.js
index 0b086ebb..968a0d16 100644
--- a/test/type.dataExtension.test.js
+++ b/test/type.dataExtension.test.js
@@ -1,4 +1,8 @@
-const assert = require('chai').assert;
+const chai = require('chai');
+const chaiFiles = require('chai-files');
+const assert = chai.assert;
+const expect = chai.expect;
+const file = chaiFiles.file;
 const cache = require('../lib/util/cache');
 const testUtils = require('./utils');
 const handler = require('../lib/index');
@@ -29,6 +33,13 @@ describe('type: dataExtension', () => {
 
                 'returned metadata was not equal expected'
             );
+            // check if MD file was created and equals expectations
+            expect(
+                file(testUtils.getActualDoc('testExisting_dataExtension', 'dataExtension'))
+            ).to.equal(
+                file(testUtils.getExpectedFile('9999999', 'dataExtension', 'retrieve', 'md'))
+            );
+
             assert.equal(
                 testUtils.getAPIHistoryLength(),
                 5,
@@ -74,6 +85,7 @@ describe('type: dataExtension', () => {
             return;
         });
         it('Should change the key during update via --changeKeyValue');
+        it('Should rename fields');
     });
     describe('Templating ================', () => {
         it('Should create a dataExtension template via retrieveAsTemplate and build it', async () => {
diff --git a/test/type.mobileKeyword.test.js b/test/type.mobileKeyword.test.js
index bac8e0ee..9f37bffc 100644
--- a/test/type.mobileKeyword.test.js
+++ b/test/type.mobileKeyword.test.js
@@ -30,12 +30,21 @@ describe('type: mobileKeyword', () => {
                 'only 1 mobileKeywords expected'
             );
             assert.deepEqual(
-                await testUtils.getActualJson('testExisting_keyword', 'mobileKeyword'),
+                await testUtils.getActualJson(
+                    '4912312345678.TESTEXISTING_KEYWORD',
+                    'mobileKeyword'
+                ),
                 await testUtils.getExpectedJson('9999999', 'mobileKeyword', 'get'),
                 'saved JSON was not equal expected'
             );
             expect(
-                file(testUtils.getActualFile('testExisting_keyword', 'mobileKeyword', 'amp'))
+                file(
+                    testUtils.getActualFile(
+                        '4912312345678.TESTEXISTING_KEYWORD',
+                        'mobileKeyword',
+                        'amp'
+                    )
+                )
             ).to.equal(file(testUtils.getExpectedFile('9999999', 'mobileKeyword', 'get', 'amp')));
             assert.equal(
                 testUtils.getAPIHistoryLength(),
@@ -51,7 +60,11 @@ describe('type: mobileKeyword', () => {
         });
         it('Should create (but not update) a mobileKeyword', async () => {
             // WHEN
-            await handler.deploy('testInstance/testBU', ['mobileKeyword'], ['testNew_keyword']);
+            await handler.deploy(
+                'testInstance/testBU',
+                ['mobileKeyword'],
+                ['4912312345678.TESTNEW_KEYWORD']
+            );
             // THEN
             assert.equal(process.exitCode, false, 'deploy should not have thrown an error');
             // get results from cache
@@ -63,12 +76,14 @@ describe('type: mobileKeyword', () => {
             );
             // confirm created item
             assert.deepEqual(
-                await testUtils.getActualJson('testNew_keyword', 'mobileKeyword'),
+                await testUtils.getActualJson('4912312345678.TESTNEW_KEYWORD', 'mobileKeyword'),
                 await testUtils.getExpectedJson('9999999', 'mobileKeyword', 'post-create'),
                 'returned JSON was not equal expected for insert mobileKeyword'
             );
             expect(
-                file(testUtils.getActualFile('testNew_keyword', 'mobileKeyword', 'amp'))
+                file(
+                    testUtils.getActualFile('4912312345678.TESTNEW_KEYWORD', 'mobileKeyword', 'amp')
+                )
             ).to.equal(
                 file(testUtils.getExpectedFile('9999999', 'mobileKeyword', 'post-create', 'amp'))
             );
@@ -92,7 +107,7 @@ describe('type: mobileKeyword', () => {
             await handler.deploy(
                 'testInstance/testBU',
                 ['mobileKeyword'],
-                ['testNew_keyword_blocked']
+                ['4912312345678.TESTNEW_KEYWORD_BLOCKED']
             );
             // THEN
             assert.equal(process.exitCode, true, 'deploy should have thrown an error');
@@ -113,7 +128,7 @@ describe('type: mobileKeyword', () => {
             const result = await handler.retrieveAsTemplate(
                 'testInstance/testBU',
                 'mobileKeyword',
-                ['testExisting_keyword'],
+                ['4912312345678.TESTEXISTING_KEYWORD'],
                 'testSourceMarket'
             );
             // WHEN
@@ -128,13 +143,20 @@ describe('type: mobileKeyword', () => {
                 'only one item expected'
             );
             assert.deepEqual(
-                await testUtils.getActualTemplateJson('testExisting_keyword', 'mobileKeyword'),
+                await testUtils.getActualTemplateJson(
+                    '4912312345678.TESTEXISTING_KEYWORD',
+                    'mobileKeyword'
+                ),
                 await testUtils.getExpectedJson('9999999', 'mobileKeyword', 'template'),
                 'returned template JSON was not equal expected'
             );
             expect(
                 file(
-                    testUtils.getActualTemplateFile('testExisting_keyword', 'mobileKeyword', 'amp')
+                    testUtils.getActualTemplateFile(
+                        '4912312345678.TESTEXISTING_KEYWORD',
+                        'mobileKeyword',
+                        'amp'
+                    )
                 )
             ).to.equal(
                 file(testUtils.getExpectedFile('9999999', 'mobileKeyword', 'template', 'amp'))
@@ -153,7 +175,7 @@ describe('type: mobileKeyword', () => {
             const result = await handler.buildTemplate(
                 'testInstance/testBU',
                 'mobileKeyword',
-                ['testExisting_keyword'],
+                ['4912312345678.TESTEXISTING_KEYWORD'],
                 'testSourceMarket'
             );
             assert.equal(process.exitCode, false, 'buildTemplate should not have thrown an error');
@@ -164,13 +186,20 @@ describe('type: mobileKeyword', () => {
                 'only one mobileKeyword expected'
             );
             assert.deepEqual(
-                await testUtils.getActualTemplateJson('testExisting_keyword', 'mobileKeyword'),
+                await testUtils.getActualTemplateJson(
+                    '4912312345678.TESTEXISTING_KEYWORD',
+                    'mobileKeyword'
+                ),
                 await testUtils.getExpectedJson('9999999', 'mobileKeyword', 'template'),
                 'returned template JSON was not equal expected'
             );
             expect(
                 file(
-                    testUtils.getActualTemplateFile('testExisting_keyword', 'mobileKeyword', 'amp')
+                    testUtils.getActualTemplateFile(
+                        '4912312345678.TESTEXISTING_KEYWORD',
+                        'mobileKeyword',
+                        'amp'
+                    )
                 )
             ).to.equal(
                 file(testUtils.getExpectedFile('9999999', 'mobileKeyword', 'template', 'amp'))
@@ -180,7 +209,7 @@ describe('type: mobileKeyword', () => {
             await handler.buildDefinition(
                 'testInstance/testBU',
                 'mobileKeyword',
-                'testExisting_keyword',
+                '4912312345678.TESTEXISTING_KEYWORD',
                 'testTargetMarket'
             );
             assert.equal(
@@ -189,12 +218,21 @@ describe('type: mobileKeyword', () => {
                 'buildDefinition should not have thrown an error'
             );
             assert.deepEqual(
-                await testUtils.getActualDeployJson('testTemplated_keyword', 'mobileKeyword'),
+                await testUtils.getActualDeployJson(
+                    '4912312345678.TESTTEMPLATED_KEYWORD',
+                    'mobileKeyword'
+                ),
                 await testUtils.getExpectedJson('9999999', 'mobileKeyword', 'build'),
                 'returned deployment JSON was not equal expected'
             );
             expect(
-                file(testUtils.getActualDeployFile('testTemplated_keyword', 'mobileKeyword', 'amp'))
+                file(
+                    testUtils.getActualDeployFile(
+                        '4912312345678.TESTTEMPLATED_KEYWORD',
+                        'mobileKeyword',
+                        'amp'
+                    )
+                )
             ).to.equal(file(testUtils.getExpectedFile('9999999', 'mobileKeyword', 'build', 'amp')));
 
             assert.equal(
@@ -209,7 +247,7 @@ describe('type: mobileKeyword', () => {
         it('Should delete the item', async () => {
             // WHEN
             const result = await handler.deleteByKey('testInstance/testBU', 'mobileKeyword', [
-                'testExisting_keyword',
+                '4912312345678.TESTEXISTING_KEYWORD',
             ]);
             // THEN
             assert.equal(process.exitCode, false, 'delete should not have thrown an error');
@@ -224,7 +262,7 @@ describe('type: mobileKeyword', () => {
             const fileList = await handler.getFilesToCommit(
                 'testInstance/testBU',
                 'mobileKeyword',
-                ['testExisting_keyword']
+                ['4912312345678.TESTEXISTING_KEYWORD']
             );
             // THEN
             assert.equal(
@@ -236,12 +274,12 @@ describe('type: mobileKeyword', () => {
 
             assert.equal(
                 fileList[0].split('\\').join('/'),
-                'retrieve/testInstance/testBU/mobileKeyword/testExisting_keyword.mobileKeyword-meta.json',
+                'retrieve/testInstance/testBU/mobileKeyword/4912312345678.TESTEXISTING_KEYWORD.mobileKeyword-meta.json',
                 'wrong JSON path'
             );
             assert.equal(
                 fileList[1].split('\\').join('/'),
-                'retrieve/testInstance/testBU/mobileKeyword/testExisting_keyword.mobileKeyword-meta.amp',
+                'retrieve/testInstance/testBU/mobileKeyword/4912312345678.TESTEXISTING_KEYWORD.mobileKeyword-meta.amp',
                 'wrong AMP path'
             );
             return;
diff --git a/test/type.user.test.js b/test/type.user.test.js
index 26be1696..697c5a5f 100644
--- a/test/type.user.test.js
+++ b/test/type.user.test.js
@@ -1,4 +1,9 @@
-const assert = require('chai').assert;
+const chai = require('chai');
+const chaiFiles = require('chai-files');
+const assert = chai.assert;
+chai.use(chaiFiles);
+const expect = chai.expect;
+const file = chaiFiles.file;
 const cache = require('../lib/util/cache');
 const testUtils = require('./utils');
 const handler = require('../lib/index');
@@ -29,6 +34,11 @@ describe('type: user', () => {
 
                 'returned metadata was not equal expected'
             );
+            // check if MD file was created and equals expectations
+            expect(file(`./docs/user/testInstance.users.md`)).to.equal(
+                file(testUtils.getExpectedFile('1111111', 'user', 'retrieve', 'md'))
+            );
+
             assert.equal(
                 testUtils.getAPIHistoryLength(),
                 6,
@@ -36,6 +46,22 @@ describe('type: user', () => {
             );
             return;
         });
+        it('Should retrieve a specific user but not run document', async () => {
+            // WHEN
+            await handler.retrieve('testInstance/_ParentBU_', ['user'], ['testExisting_user']);
+            // THEN
+            assert.equal(process.exitCode, false, 'retrieve should not have thrown an error');
+
+            // because user is single-document-type we would not want to find an md file when we retrieve specific keys. only the generic retrieve updates it
+            expect(file(`./docs/user/testInstance.users.md`)).to.not.exist;
+
+            assert.equal(
+                testUtils.getAPIHistoryLength(),
+                4,
+                'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
+            );
+            return;
+        });
     });
     describe('Deploy ================', () => {
         beforeEach(() => {
@@ -74,6 +100,9 @@ describe('type: user', () => {
                 await testUtils.getExpectedJson('1111111', 'user', 'update'),
                 'returned metadata was not equal expected for update'
             );
+            // because user is single-document-type we would not want to find an md file getting created by deploy. only retrieve updates it
+            expect(file(`./docs/user/testInstance.users.md`)).to.not.exist;
+
             assert.equal(
                 testUtils.getAPIHistoryLength(),
                 9,
diff --git a/test/utils.js b/test/utils.js
index 5f1e6480..6f0a628e 100644
--- a/test/utils.js
+++ b/test/utils.js
@@ -22,6 +22,16 @@ const resourceFactory = require('./resourceFactory');
  */
 exports.getActualJson = (customerKey, type, buName = 'testBU') =>
     File.readJSON(`./retrieve/testInstance/${buName}/${type}/${customerKey}.${type}-meta.json`);
+/**
+ * gets file from Retrieve folder
+ *
+ * @param {string} customerKey of metadata
+ * @param {string} type of metadata
+ * @param {string} [buName] used when we need to test on ParentBU
+ * @returns {Promise.<string>} file in string form
+ */
+exports.getActualDoc = (customerKey, type, buName = 'testBU') =>
+    `./retrieve/testInstance/${buName}/${type}/${customerKey}.${type}-doc.md`;
 /**
  * gets file from Retrieve folder
  *