mcdev 3.0.3 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.eslintrc.json +1 -1
  2. package/.github/ISSUE_TEMPLATE/bug.yml +75 -0
  3. package/.github/ISSUE_TEMPLATE/task.md +1 -1
  4. package/.issuetracker +11 -3
  5. package/.vscode/settings.json +3 -3
  6. package/CHANGELOG.md +66 -0
  7. package/README.md +245 -141
  8. package/boilerplate/config.json +3 -2
  9. package/docs/dist/documentation.md +799 -338
  10. package/lib/Deployer.js +4 -1
  11. package/lib/MetadataTypeDefinitions.js +1 -0
  12. package/lib/MetadataTypeInfo.js +1 -0
  13. package/lib/Retriever.js +30 -14
  14. package/lib/cli.js +298 -0
  15. package/lib/index.js +773 -1019
  16. package/lib/metadataTypes/AccountUser.js +389 -0
  17. package/lib/metadataTypes/Asset.js +8 -7
  18. package/lib/metadataTypes/Automation.js +121 -56
  19. package/lib/metadataTypes/DataExtension.js +133 -97
  20. package/lib/metadataTypes/DataExtensionField.js +134 -4
  21. package/lib/metadataTypes/DataExtract.js +9 -5
  22. package/lib/metadataTypes/EventDefinition.js +9 -5
  23. package/lib/metadataTypes/FileTransfer.js +9 -5
  24. package/lib/metadataTypes/ImportFile.js +13 -12
  25. package/lib/metadataTypes/MetadataType.js +41 -33
  26. package/lib/metadataTypes/Query.js +2 -3
  27. package/lib/metadataTypes/Role.js +13 -8
  28. package/lib/metadataTypes/Script.js +2 -2
  29. package/lib/metadataTypes/definitions/AccountUser.definition.js +227 -0
  30. package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
  31. package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -1
  32. package/lib/metadataTypes/definitions/ImportFile.definition.js +2 -1
  33. package/lib/metadataTypes/definitions/Script.definition.js +5 -5
  34. package/lib/retrieveChangelog.js +96 -0
  35. package/lib/util/cli.js +4 -6
  36. package/lib/util/init.git.js +2 -1
  37. package/lib/util/util.js +17 -0
  38. package/package.json +18 -22
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  40. package/img/README.md/troubleshoot-nodejs-postinstall.jpg +0 -0
  41. package/postinstall.js +0 -41
@@ -0,0 +1,227 @@
1
+ module.exports = {
2
+ bodyIteratorField: 'Results',
3
+ dependencies: [],
4
+ folderType: null,
5
+ hasExtended: false,
6
+ idField: 'ID',
7
+ keyField: 'CustomerKey',
8
+ nameField: 'Name',
9
+ type: 'accountUser',
10
+ typeDescription: 'Marketing Cloud users',
11
+ typeName: 'User',
12
+ typeRetrieveByDefault: false,
13
+ fields: {
14
+ AccountUserID: {
15
+ isCreateable: null,
16
+ isUpdateable: null,
17
+ retrieving: true,
18
+ template: false,
19
+ },
20
+ ActiveFlag: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
21
+ AssociatedBusinessUnits: {
22
+ isCreateable: null,
23
+ isUpdateable: null,
24
+ retrieving: false,
25
+ template: null,
26
+ },
27
+ BusinessUnit: {
28
+ isCreateable: null,
29
+ isUpdateable: null,
30
+ retrieving: false,
31
+ template: null,
32
+ },
33
+ ChallengeAnswer: {
34
+ isCreateable: null,
35
+ isUpdateable: null,
36
+ retrieving: true,
37
+ template: false,
38
+ },
39
+ ChallengePhrase: {
40
+ isCreateable: null,
41
+ isUpdateable: null,
42
+ retrieving: true,
43
+ template: false,
44
+ },
45
+ Client: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
46
+ CorrelationID: {
47
+ isCreateable: null,
48
+ isUpdateable: null,
49
+ retrieving: false,
50
+ template: null,
51
+ },
52
+ CreatedDate: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
53
+ CustomerKey: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
54
+ DefaultApplication: {
55
+ isCreateable: null,
56
+ isUpdateable: null,
57
+ retrieving: false,
58
+ template: null,
59
+ },
60
+ DefaultBusinessUnit: {
61
+ isCreateable: null,
62
+ isUpdateable: null,
63
+ retrieving: true,
64
+ template: false,
65
+ },
66
+ DefaultBusinessUnitObject: {
67
+ isCreateable: null,
68
+ isUpdateable: null,
69
+ retrieving: false,
70
+ template: null,
71
+ },
72
+ Delete: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
73
+ Email: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
74
+ ID: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
75
+ IsAPIUser: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
76
+ IsLocked: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
77
+ LanguageLocale: {
78
+ isCreateable: null,
79
+ isUpdateable: null,
80
+ retrieving: false,
81
+ template: null,
82
+ },
83
+ LastSuccessfulLogin: {
84
+ isCreateable: null,
85
+ isUpdateable: null,
86
+ retrieving: true,
87
+ template: false,
88
+ },
89
+ Locale: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
90
+ ModifiedDate: {
91
+ isCreateable: null,
92
+ isUpdateable: null,
93
+ retrieving: true,
94
+ template: false,
95
+ },
96
+ MustChangePassword: {
97
+ isCreateable: null,
98
+ isUpdateable: null,
99
+ retrieving: true,
100
+ template: false,
101
+ },
102
+ Name: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
103
+ NotificationEmailAddress: {
104
+ isCreateable: null,
105
+ isUpdateable: null,
106
+ retrieving: true,
107
+ template: false,
108
+ },
109
+ ObjectID: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
110
+ ObjectState: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
111
+ Owner: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
112
+ PartnerKey: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
113
+ PartnerProperties: {
114
+ isCreateable: null,
115
+ isUpdateable: null,
116
+ retrieving: false,
117
+ template: null,
118
+ },
119
+ Password: { isCreateable: null, isUpdateable: null, retrieving: false, template: false },
120
+ Roles: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
121
+ 'Roles.Role': {
122
+ skipValidation: false,
123
+ },
124
+ 'Roles.Role[].Client': {
125
+ skipValidation: false,
126
+ },
127
+ SsoIdentities: {
128
+ isCreateable: null,
129
+ isUpdateable: null,
130
+ retrieving: false,
131
+ template: null,
132
+ },
133
+ TimeZone: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
134
+ Unlock: { isCreateable: null, isUpdateable: null, retrieving: false, template: null },
135
+ UserID: { isCreateable: null, isUpdateable: null, retrieving: true, template: false },
136
+ UserPermissions: {
137
+ isCreateable: null,
138
+ isUpdateable: null,
139
+ retrieving: true,
140
+ template: false,
141
+ },
142
+ 'UserPermissions.PartnerKey': {
143
+ isCreateable: null,
144
+ isUpdateable: null,
145
+ retrieving: false,
146
+ template: false,
147
+ },
148
+ 'UserPermissions.ID': {
149
+ skipValidation: true,
150
+ },
151
+ 'UserPermissions.ObjectID': {
152
+ isCreateable: null,
153
+ isUpdateable: null,
154
+ retrieving: false,
155
+ template: false,
156
+ },
157
+ 'UserPermissions.Name': {
158
+ isCreateable: null,
159
+ isUpdateable: null,
160
+ retrieving: false,
161
+ template: false,
162
+ },
163
+ 'UserPermissions.Value': {
164
+ isCreateable: null,
165
+ isUpdateable: null,
166
+ retrieving: false,
167
+ template: false,
168
+ },
169
+ 'UserPermissions.Description': {
170
+ isCreateable: null,
171
+ isUpdateable: null,
172
+ retrieving: false,
173
+ template: false,
174
+ },
175
+ 'UserPermissions.Delete': {
176
+ isCreateable: null,
177
+ isUpdateable: null,
178
+ retrieving: false,
179
+ template: false,
180
+ },
181
+ 'UserPermissions[].PartnerKey': {
182
+ isCreateable: null,
183
+ isUpdateable: null,
184
+ retrieving: false,
185
+ template: false,
186
+ },
187
+ 'UserPermissions[].ID': {
188
+ skipValidation: true,
189
+ },
190
+ 'UserPermissions[].ObjectID': {
191
+ isCreateable: null,
192
+ isUpdateable: null,
193
+ retrieving: false,
194
+ template: false,
195
+ },
196
+ 'UserPermissions[].Name': {
197
+ isCreateable: null,
198
+ isUpdateable: null,
199
+ retrieving: false,
200
+ template: false,
201
+ },
202
+ 'UserPermissions[].Value': {
203
+ isCreateable: null,
204
+ isUpdateable: null,
205
+ retrieving: false,
206
+ template: false,
207
+ },
208
+ 'UserPermissions[].Description': {
209
+ isCreateable: null,
210
+ isUpdateable: null,
211
+ retrieving: false,
212
+ template: false,
213
+ },
214
+ 'UserPermissions[].Delete': {
215
+ isCreateable: null,
216
+ isUpdateable: null,
217
+ retrieving: false,
218
+ template: false,
219
+ },
220
+ type__c: {
221
+ skipValidation: true,
222
+ },
223
+ AssociatedBusinessUnits__c: {
224
+ skipValidation: true,
225
+ },
226
+ },
227
+ };
@@ -677,6 +677,7 @@ module.exports = {
677
677
  ],
678
678
  },
679
679
  typeMapping: {
680
+ ai: 16,
680
681
  psd: 17,
681
682
  pdd: 18,
682
683
  eps: 19,
@@ -167,7 +167,7 @@ module.exports = {
167
167
  ModifiedDate: {
168
168
  isCreateable: false,
169
169
  isUpdateable: false,
170
- retrieving: false,
170
+ retrieving: true,
171
171
  template: false,
172
172
  },
173
173
  Name: {
@@ -1,7 +1,8 @@
1
1
  module.exports = {
2
2
  bodyIteratorField: 'items',
3
- dependencies: ['ftpLocation', 'dataExtension', 'list'],
3
+ dependencies: ['folder', 'ftpLocation', 'dataExtension', 'list'], // folder has to be kept in here to resolve lists correctly
4
4
  destinationObjectTypeMapping: {
5
+ unknown: 783,
5
6
  DataExtension: 310,
6
7
  List: 13,
7
8
  SMS: 584,
@@ -21,7 +21,7 @@ module.exports = {
21
21
  createdBy: {
22
22
  isCreateable: false,
23
23
  isUpdateable: false,
24
- retrieving: true,
24
+ retrieving: false,
25
25
  template: false,
26
26
  },
27
27
  createdDate: {
@@ -51,7 +51,7 @@ module.exports = {
51
51
  modifiedBy: {
52
52
  isCreateable: false,
53
53
  isUpdateable: false,
54
- retrieving: true,
54
+ retrieving: false,
55
55
  template: false,
56
56
  },
57
57
  modifiedDate: {
@@ -79,9 +79,9 @@ module.exports = {
79
79
  template: false,
80
80
  },
81
81
  status: {
82
- isCreateable: null,
83
- isUpdateable: null,
84
- retrieving: true,
82
+ isCreateable: false,
83
+ isUpdateable: false,
84
+ retrieving: false,
85
85
  template: false,
86
86
  },
87
87
  statusId: {
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * sample file on how to retrieve a simple changelog to use in GUIs or automated processing of any kind
6
+ * @example
7
+ [{
8
+ name: 'deName',
9
+ key: 'deKey',
10
+ t: 'dataExtension',
11
+ cd: '2020-05-06T00:16:00.737',
12
+ cb: 'name of creator',
13
+ ld: '2020-05-06T00:16:00.737',
14
+ lb: 'name of lastmodified'
15
+ }]
16
+ */
17
+
18
+ const mcdev = require('./index');
19
+ const Definition = require('./MetadataTypeDefinitions');
20
+ const MetadataType = require('./MetadataTypeInfo');
21
+
22
+ // disable cli logs
23
+ // mcdev._setLoggingLevel({ silent: true });
24
+
25
+ const customDefinition = {
26
+ automation: {
27
+ keyField: 'CustomerKey',
28
+ nameField: 'Name',
29
+ createdDateField: 'CreatedDate',
30
+ createdNameField: 'CreatedBy',
31
+ lastmodDateField: 'LastSaveDate',
32
+ lastmodNameField: 'LastSavedBy',
33
+ },
34
+ dataExtension: {
35
+ keyField: 'CustomerKey',
36
+ nameField: 'Name',
37
+ createdDateField: 'CreatedDate',
38
+ createdNameField: null,
39
+ lastmodDateField: 'ModifiedDate',
40
+ lastmodNameField: null,
41
+ },
42
+ };
43
+ (async function () {
44
+ // get userid>name mapping
45
+ const userList = (await mcdev.retrieve('ACN-Learning/_ParentBU_', 'accountUser', true))
46
+ .accountUser;
47
+ // reduce userList to simple id-name map
48
+ Object.keys(userList).forEach((key) => {
49
+ userList[userList[key].ID] = userList[key].Name;
50
+ delete userList[key];
51
+ });
52
+
53
+ // get changed metadata
54
+ const changelogList = await mcdev.retrieve('ACN-Learning/MCDEV_Training_Source', null, true);
55
+ const allMetadata = [];
56
+ Object.keys(changelogList).map((type) => {
57
+ if (changelogList[type]) {
58
+ const def = customDefinition[type] || Definition[type];
59
+ allMetadata.push(
60
+ ...Object.keys(changelogList[type]).map((key) => {
61
+ const item = changelogList[type][key];
62
+ if (
63
+ MetadataType[type].isFiltered(item, true) ||
64
+ MetadataType[type].isFiltered(item, false)
65
+ ) {
66
+ return;
67
+ }
68
+
69
+ const listEntry = {
70
+ name: item[def.nameField],
71
+ key: item[def.keyField],
72
+ t: type,
73
+ cd: item[def.createdDateField],
74
+ cb: getUserName(userList, item, def.createdNameField),
75
+ ld: item[def.lastmodDateField],
76
+ lb: getUserName(userList, item, def.lastmodNameField),
77
+ };
78
+ return listEntry;
79
+ })
80
+ );
81
+ }
82
+ });
83
+ const finalResult = allMetadata.filter((item) => undefined !== item);
84
+ console.log('finalResult', finalResult);
85
+ })();
86
+
87
+ /**
88
+ *
89
+ * @param {object<string,string>} userList user-id > user-name map
90
+ * @param {object<string,string>} item single metadata item
91
+ * @param {string} fieldname name of field containing the info
92
+ * @returns {string} username or user id or 'n/a'
93
+ */
94
+ function getUserName(userList, item, fieldname) {
95
+ return userList[item[fieldname]] || item[fieldname] || 'n/a';
96
+ }
package/lib/util/cli.js CHANGED
@@ -427,9 +427,8 @@ const Cli = {
427
427
  subtype,
428
428
  type: MetadataDefinitions[el].type + '-' + subtype,
429
429
  mainType: MetadataDefinitions[el].type,
430
- typeRetrieveByDefault: MetadataDefinitions[
431
- el
432
- ].typeRetrieveByDefault.includes(subtype),
430
+ typeRetrieveByDefault:
431
+ MetadataDefinitions[el].typeRetrieveByDefault.includes(subtype),
433
432
  });
434
433
  }
435
434
  }
@@ -560,9 +559,8 @@ const Cli = {
560
559
  let lastCountdown = MetadataDefinitions[el].subTypes.length;
561
560
  for (const subtype of MetadataDefinitions[el].subTypes) {
562
561
  lastCountdown--;
563
- const subTypeRetrieveByDefault = MetadataDefinitions[
564
- el
565
- ].typeRetrieveByDefault.includes(subtype);
562
+ const subTypeRetrieveByDefault =
563
+ MetadataDefinitions[el].typeRetrieveByDefault.includes(subtype);
566
564
  const definition =
567
565
  ' ' + MetadataDefinitions[el].extendedSubTypes[subtype].join(', ');
568
566
  typeChoices.push({
@@ -229,7 +229,8 @@ const Init = {
229
229
  // eslint-disable-next-line require-jsdoc
230
230
  validate: function (value) {
231
231
  value = value.trim();
232
- const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
232
+ const regex =
233
+ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
233
234
  if (!value || !regex.test(String(value).toLowerCase())) {
234
235
  return 'Please enter valid email';
235
236
  }
package/lib/util/util.js CHANGED
@@ -110,6 +110,23 @@ const Util = {
110
110
  'workflows_read',
111
111
  'workflows_write',
112
112
  ],
113
+ /**
114
+ * SFMC accepts multiple true values for Boolean attributes for which we are checking here
115
+ * @param {*} attrValue value
116
+ * @returns {boolean} attribute value == true ? true : false
117
+ */
118
+ isTrue(attrValue) {
119
+ return ['true', 'TRUE', 'True', '1', 1, 'Y'].includes(attrValue);
120
+ },
121
+ /**
122
+ * SFMC accepts multiple false values for Boolean attributes for which we are checking here
123
+ * @param {*} attrValue value
124
+ * @returns {boolean} attribute value == false ? true : false
125
+ */
126
+ isFalse(attrValue) {
127
+ return ['false', 'FALSE', 'False', '0', 0, 'N'].includes(attrValue);
128
+ },
129
+
113
130
  /**
114
131
  * defines how the properties.json should look like
115
132
  * used for creating a template and for checking if variables are set
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "3.0.3",
3
+ "version": "3.1.3",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "main": "./lib/index.js",
21
21
  "bin": {
22
- "mcdev": "./lib/index.js"
22
+ "mcdev": "./lib/cli.js"
23
23
  },
24
24
  "engines": {
25
25
  "node": ">=14.16"
@@ -27,7 +27,6 @@
27
27
  "scripts": {
28
28
  "start": "node lib/index.js",
29
29
  "mcdev": "node lib/index.js",
30
- "postinstall": "node postinstall.js",
31
30
  "build": "run-s lint docs",
32
31
  "debug": "node --nolazy --inspect-brk=9229 lib/index.js",
33
32
  "docs": "jsdoc2md --files lib/**/*.js > docs/dist/documentation.md",
@@ -38,34 +37,34 @@
38
37
  },
39
38
  "dependencies": {
40
39
  "bluebird": "3.7.2",
41
- "cli-progress": "3.9.0",
40
+ "cli-progress": "3.10.0",
42
41
  "command-exists": "1.2.9",
43
42
  "console.table": "0.10.0",
44
- "fs-extra": "9.1.0",
45
- "inquirer": "8.0.0",
43
+ "fs-extra": "10.0.0",
44
+ "inquirer": "8.2.0",
46
45
  "json-to-table": "4.2.1",
47
- "mustache": "4.1.0",
48
- "prettier": "2.2.1",
49
- "semver": "5.7.1",
46
+ "mustache": "4.2.0",
47
+ "prettier": "2.5.1",
48
+ "semver": "7.3.5",
50
49
  "sfmc-fuelsdk-node": "2.4.0",
51
- "simple-git": "2.37.0",
50
+ "simple-git": "2.48.0",
52
51
  "sql-formatter-plus": "1.3.6",
53
52
  "toposort": "2.0.2",
54
53
  "update-notifier-git": "5.0.3",
55
- "winston": "3.3.3",
56
- "yargs": "16.2.0"
54
+ "winston": "3.3.4",
55
+ "yargs": "17.3.0"
57
56
  },
58
57
  "devDependencies": {
59
58
  "chai": "4.3.4",
60
- "eslint": "7.32.0",
59
+ "eslint": "8.4.1",
61
60
  "eslint-config-prettier": "8.3.0",
62
61
  "eslint-config-ssjs": "1.1.11",
63
- "eslint-plugin-mocha": "9.0.0",
64
- "eslint-plugin-prettier": "3.4.0",
65
- "husky": "5.1.3",
66
- "jsdoc-to-markdown": "7.0.1",
67
- "lint-staged": "10.5.4",
68
- "mocha": "8.3.2",
62
+ "eslint-plugin-mocha": "10.0.1",
63
+ "eslint-plugin-prettier": "4.0.0",
64
+ "husky": "7.0.4",
65
+ "jsdoc-to-markdown": "7.1.0",
66
+ "lint-staged": "12.1.2",
67
+ "mocha": "9.1.3",
69
68
  "npm-check": "5.9.2",
70
69
  "npm-run-all": "4.1.5"
71
70
  },
@@ -73,9 +72,6 @@
73
72
  "*.{js,jsx,ts,tsx}": [
74
73
  "eslint --fix",
75
74
  "prettier --write"
76
- ],
77
- "*.{md}": [
78
- "prettier --write"
79
75
  ]
80
76
  }
81
77
  }
@@ -1,30 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Create a report to help us improve
4
- title: "[BUG] "
5
- labels: NEW, bug
6
- assignees: ''
7
-
8
- ---
9
-
10
- **Describe the bug**
11
- A clear and concise description of what the bug is.
12
-
13
- **To Reproduce**
14
- Steps to reproduce the behavior:
15
- 1. Go to '...'
16
- 2. Click on '....'
17
- 3. Scroll down to '....'
18
- 4. See error
19
-
20
- **Expected behavior**
21
- A clear and concise description of what you expected to happen.
22
-
23
- **Screenshots**
24
- If applicable, add screenshots to help explain your problem.
25
-
26
- **Versions:**
27
- - mcdev (mcdev --version):
28
- - npm (npm --version):
29
- - node (node --version):
30
- - Operating system (Windows/Mac):
package/postinstall.js DELETED
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
- const greenOnBlack = '\x1b[32m\x1b[40m';
4
- const highlight = '\x1b[7m';
5
- const reset = '\x1b[0m';
6
- console.log(greenOnBlack); // green on black
7
- console.log('Congratulations: You installed Accenture Salesforce Marketing Cloud DevTools!');
8
- console.log(
9
- `If you have existing Accenture SFMC DevTools projects please run ${highlight}mcdev upgrade${reset}${greenOnBlack} in each of your project directories.`
10
- );
11
-
12
- console.log(reset); // reset cli colors
13
-
14
- /*
15
- Color reference
16
- Reset = "\x1b[0m"
17
- Bright = "\x1b[1m"
18
- Dim = "\x1b[2m"
19
- Underscore = "\x1b[4m"
20
- Blink = "\x1b[5m"
21
- Reverse = "\x1b[7m"
22
- Hidden = "\x1b[8m"
23
-
24
- FgBlack = "\x1b[30m"
25
- FgRed = "\x1b[31m"
26
- FgGreen = "\x1b[32m"
27
- FgYellow = "\x1b[33m"
28
- FgBlue = "\x1b[34m"
29
- FgMagenta = "\x1b[35m"
30
- FgCyan = "\x1b[36m"
31
- FgWhite = "\x1b[37m"
32
-
33
- BgBlack = "\x1b[40m"
34
- BgRed = "\x1b[41m"
35
- BgGreen = "\x1b[42m"
36
- BgYellow = "\x1b[43m"
37
- BgBlue = "\x1b[44m"
38
- BgMagenta = "\x1b[45m"
39
- BgCyan = "\x1b[46m"
40
- BgWhite = "\x1b[47m"
41
- */