configorama 0.6.1 → 0.6.2

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.
package/cli.js CHANGED
@@ -4,6 +4,7 @@ const fs = require('fs')
4
4
  const minimist = require('minimist')
5
5
  const Configorama = require('./src/main')
6
6
  const deepLog = require('./src/utils/deep-log')
7
+ const { logHeader } = require('./src/utils/logs')
7
8
 
8
9
  // Parse command line arguments
9
10
  const argv = minimist(process.argv.slice(2), {
@@ -79,7 +80,7 @@ const options = {
79
80
  }
80
81
 
81
82
  if (options.dynamicArgs.verbose) {
82
- console.log('───────────── Input Options ──────────────────────')
83
+ logHeader('Config Input Options')
83
84
  const dynamicArgs = options.dynamicArgs || {}
84
85
  const {
85
86
  _,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "configorama",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Variable support for configuration files",
5
- "main": "lib/index.js",
5
+ "main": "src/index.js",
6
6
  "files": [
7
7
  "cli.js",
8
8
  "src",
@@ -34,6 +34,7 @@
34
34
  "url": "https://github.com/DavidWells/configorama"
35
35
  },
36
36
  "dependencies": {
37
+ "@davidwells/box-logger": "^1.0.10",
37
38
  "@iarna/toml": "^2.2.5",
38
39
  "dot-prop": "^5.3.0",
39
40
  "find-up": "^3.0.0",
@@ -60,6 +61,7 @@
60
61
  "lodash.split": "^4.4.2",
61
62
  "minimist": "^1.2.8",
62
63
  "promise.prototype.finally": "^3.1.8",
64
+ "safe-chalk": "^1.0.0",
63
65
  "sync-rpc": "^1.3.6",
64
66
  "traverse": "^0.6.8"
65
67
  },
package/src/main.js CHANGED
@@ -12,6 +12,8 @@ const promiseFinallyShim = require('promise.prototype.finally').shim()
12
12
  const findUp = require('find-up')
13
13
  const traverse = require('traverse')
14
14
  const dotProp = require('dot-prop')
15
+ const chalk = require('./utils/chalk')
16
+
15
17
  /* Default Value resolvers */
16
18
  const getValueFromString = require('./resolvers/valueFromString')
17
19
  const getValueFromNumber = require('./resolvers/valueFromNumber')
@@ -48,6 +50,8 @@ const { encodeUnknown, decodeUnknown } = require('./utils/unknownValues')
48
50
  const { mergeByKeys } = require('./utils/mergeByKeys')
49
51
  const { arrayToJsonPath } = require('./utils/arrayToJsonPath')
50
52
  const { findNestedVariables } = require('./utils/find-nested-variables')
53
+ const { makeBox } = require('@davidwells/box-logger')
54
+ const { logHeader } = require('./utils/logs')
51
55
  /**
52
56
  * Maintainer's notes:
53
57
  *
@@ -407,7 +411,7 @@ class Configorama {
407
411
  }
408
412
 
409
413
  if (VERBOSE) {
410
- console.log('───────────── Input Config ──────────────────────')
414
+ logHeader('Config Input before processing')
411
415
  console.log()
412
416
  deepLog(this.originalConfig)
413
417
  console.log()
@@ -415,60 +419,229 @@ class Configorama {
415
419
 
416
420
  if (VERBOSE || showFoundVariables) {
417
421
  const foundVariables = []
418
- let loggedHeader = false
422
+ const variableData = {}
423
+ let matchCount = 1
419
424
  traverse(this.originalConfig).forEach(function (rawValue) {
420
425
  if (typeof rawValue === 'string' && rawValue.match(variableSyntax)) {
421
426
  const configValuePath = this.path.join('.')
422
427
  if (configValuePath.endsWith('Fn::Sub')) {
423
428
  return
424
429
  }
425
-
426
-
427
- if (!loggedHeader) {
428
- console.log('───────────── Variables Detected ──────────────────────')
429
- console.log()
430
- loggedHeader = true
431
- }
432
430
 
433
431
  const nested = findNestedVariables(rawValue, variableSyntax, variablesKnownTypes, configValuePath)
434
432
  /*
435
- console.log(nested)
433
+ console.log('traverse nested result', nested)
436
434
  /** */
437
435
 
438
- console.log(`▷ Path: ${configValuePath}`)
439
- console.log('\n Key/value:')
440
- console.log(` ${configValuePath}: ${rawValue}`)
441
- if (nested.length > 0) {
442
- const nestedCount = nested.length - 1
443
- console.log('\n Variable:')
444
- console.log(` ${nested[nested.length - 1].fullMatch}`)
445
-
446
- if (nestedCount) {
447
- console.log(`\n Contains ${nestedCount} nested values.`)
436
+ // console.log(`▷ Path: ${configValuePath}`)
437
+ // console.log('\n Key/value:')
438
+ // console.log(` ${configValuePath}: ${rawValue}`)
439
+ const lastItem = nested[nested.length - 1]
440
+ const lastKeyPath = this.path[this.path.length - 1]
441
+ const itemKey = (lastKeyPath.match(/[\d+]$/)) ? `${this.path[this.path.length - 2]}[${lastKeyPath}]` : lastKeyPath
442
+ const key = lastItem.fullMatch
443
+ const varData = {
444
+ path: configValuePath,
445
+ key: itemKey,
446
+ value: rawValue,
447
+ variable: lastItem.fullMatch,
448
+ isRequired: false,
449
+ defaultValue: undefined,
450
+ matchIndex: matchCount++,
451
+ // hasFallback: false,
452
+ resolveOrder: [],
453
+ resolveDetails: nested,
454
+ }
455
+
456
+ function calculateResolveOrder(item) {
457
+ if (item && item.fallbackValues) {
458
+ let hasResolvedFallback
459
+ const order = ([item.valueBeforeFallback]).concat(item.fallbackValues.map((f, i) => {
460
+ if (f.fallbackValues) {
461
+ const [nestedOrder, nestedResolvedFallback] = calculateResolveOrder(f)
462
+ if (!hasResolvedFallback && nestedResolvedFallback) {
463
+ hasResolvedFallback = nestedResolvedFallback
464
+ }
465
+ return nestedOrder // Return just the order part
466
+ }
467
+ if (!hasResolvedFallback && f.isResolvedFallback) {
468
+ hasResolvedFallback = f.stringValue
469
+ }
470
+ return `${f.stringValue || f.variable}${f.isResolvedFallback ? ' (Resolved default fallback)' : ''}`
471
+ })).flat()
472
+
473
+ return [order, hasResolvedFallback]
448
474
  }
449
-
450
- // non mutate remove last
451
- const removeLast = (nested.length > 1) ? nested.slice(0, -1) : nested
452
- console.log()
453
- removeLast.forEach((v) => {
454
- if (v.hasFallback) {
455
- console.log(' Resolve order:')
456
- console.log(` 1. ${v.valueBeforeFallback}`)
457
- v.fallbackValues.forEach((f, i) => {
458
- console.log(` ${i + 2}. ${f.fullMatch}${f.isFallback ? ' (Fallback string value)' : ''}`)
459
- })
460
- }
461
- })
475
+ return [[item.variable], false] // Return array instead of just the value
462
476
  }
463
- console.log()
477
+
478
+ const [resolveOrder, hasResolvedFallback] = calculateResolveOrder(lastItem)
479
+ varData.resolveOrder = resolveOrder
480
+
481
+ if (hasResolvedFallback) {
482
+ varData.defaultValue = hasResolvedFallback
483
+ }
484
+
485
+
486
+ if (!varData.defaultValue) {
487
+ varData.isRequired = true
488
+ }
489
+
490
+
491
+ if (varData.resolveOrder.length > 1) {
492
+ varData.hasFallback = true
493
+ }
494
+
495
+ variableData[key] = (variableData[key] || []).concat(varData)
496
+
464
497
  foundVariables.push(rawValue)
465
498
  }
466
499
  })
467
500
 
468
- console.log(`───────────── Found ${foundVariables.length} Variables ──────────────────────`)
469
- console.log()
470
- deepLog(foundVariables)
471
- console.log()
501
+
502
+ if (!foundVariables.length) {
503
+ logHeader('No Variables Found in Config')
504
+ if (this.configFilePath) {
505
+ console.log(`File: ${this.configFilePath}`)
506
+ }
507
+
508
+ console.log(`\nVariable syntax: `, variableSyntax)
509
+
510
+ const varTypes = Object.keys(this.variableTypes)
511
+ if (varTypes.length) {
512
+ const exclude = ['fallthrough', 'deep']
513
+ console.log('\nAllowed variable types:')
514
+ varTypes.filter((v) => v.type !== 'fallthrough').forEach((v) => {
515
+ const vData = this.variableTypes[v]
516
+ if (exclude.includes(vData.type)) {
517
+ return
518
+ }
519
+ console.log(` - ${vData.type}: `, vData.match)
520
+ })
521
+ }
522
+ console.log()
523
+ }
524
+
525
+ // make foundVariables array unique
526
+ const finalFoundVariables = [...new Set(foundVariables)]
527
+ if (finalFoundVariables.length > 0) {
528
+ const varKeys = Object.keys(variableData)
529
+ const fileName = this.configFilePath ? ` in ${this.configFilePath}` : ''
530
+
531
+ logHeader(`Found ${varKeys.length} Variables${fileName}`)
532
+
533
+ // deepLog('variableData', variableData)
534
+
535
+ if (varKeys.length) {
536
+ console.log()
537
+ const longestKey = varKeys.reduce((acc, k) => {
538
+ return Math.max(acc, k.length)
539
+ }, 0)
540
+ console.log(varKeys.map((k) => {
541
+ const placesWord = variableData[k].length > 1 ? 'places' : 'place'
542
+ return `- ${k.padEnd(longestKey).padEnd(longestKey + 10)} referenced ${variableData[k].length} ${placesWord}`
543
+ }).join('\n'))
544
+ console.log()
545
+ }
546
+
547
+ logHeader('Variable Details')
548
+
549
+ const indent = ''
550
+ varKeys.forEach((key, i) => {
551
+ const variableInstances = variableData[key]
552
+
553
+ const firstInstance = variableInstances[0]
554
+
555
+ let requiredText = ''
556
+ let defaultValueSrc = ''
557
+ if (!firstInstance.defaultValue) {
558
+ // console.log('no default value', firstInstance)
559
+ /* Check if the fallback variable is a self reference */
560
+ const hasDotPropOrSelf = variableInstances.reduce((acc, v) => {
561
+ const dotProp = v.resolveDetails.find((d) => d.varType === 'dot.prop')
562
+ if (dotProp) {
563
+ acc.push(dotProp)
564
+ }
565
+ if (v.resolveDetails && v.resolveDetails.length === 1 && v.resolveDetails[0].varType === 'self:') {
566
+ // console.log('dot.prop', v.resolveDetails)
567
+ acc.push(v.resolveDetails[0])
568
+ }
569
+ return acc
570
+ }, [])
571
+ // console.log('hasDotPropOrSelf', hasDotPropOrSelf)
572
+ if (!hasDotPropOrSelf.length) {
573
+ requiredText = '[Required] '
574
+ } else {
575
+ const fallBackValues = variableInstances.filter((v) => v.resolveDetails.find((d) => d.hasFallback)).map((v) => v.resolveDetails)
576
+ // console.log('fallBackValues', fallBackValues)
577
+ if (fallBackValues.length) {
578
+ // console.log('fallBackValues.resolveDetails', fallBackValues)
579
+ }
580
+
581
+ const cleanPath = hasDotPropOrSelf[0].variable.replace('self:', '')
582
+ defaultValueSrc = cleanPath
583
+ // Find the dot prop value in the original config
584
+ const dotPropValue = dotProp.get(this.originalConfig, cleanPath)
585
+ // console.log('dotPropValue', dotPropValue)
586
+ if (typeof dotPropValue !== 'undefined') {
587
+ requiredText = ''
588
+ const niceString = typeof dotPropValue === 'object' ? JSON.stringify(dotPropValue) : dotPropValue
589
+ // truncate niceString to 100 characters
590
+ const truncatedString = niceString.length > 100 ? niceString.substring(0, 90) + '...' : niceString
591
+ firstInstance.defaultValue = truncatedString
592
+ }
593
+ }
594
+ //this.originalConfig[key] = undefined
595
+ }
596
+ const spacing = ' '
597
+ const titleText = `Variable:${spacing}`
598
+ const reqText = (requiredText) ? `${chalk.red.bold(requiredText)}\n` : ''
599
+ let varMsg = `${reqText}`
600
+ const VALUE_HEX = '#899499' // '#708090'
601
+ const keyChalk = chalk.whiteBright
602
+ const valueChalk = chalk.hex(VALUE_HEX)
603
+
604
+ if (firstInstance.defaultValue) {
605
+ const defaultValueText = `${indent}${keyChalk(`DefaultValue:`.padEnd(titleText.length, ' '))}`
606
+ // ensure padding is even
607
+ varMsg += `${defaultValueText} ${valueChalk(firstInstance.defaultValue)}`
608
+ }
609
+
610
+ if(defaultValueSrc) {
611
+ varMsg += `\n${indent}${keyChalk('DefaultValue path:'.padEnd(titleText.length, ' '))} `
612
+ varMsg += `${valueChalk(defaultValueSrc)}`
613
+ }
614
+
615
+ if (firstInstance.resolveOrder.length > 1) {
616
+ varMsg += `\n${indent}${keyChalk('Resolve Order:'.padEnd(titleText.length, ' '))}`
617
+ const resolveOrder = firstInstance.resolveOrder.join(', ')
618
+ varMsg += ` ${valueChalk(resolveOrder)}`
619
+ }
620
+
621
+ let locationRender = valueChalk(variableInstances[0].path)
622
+
623
+ let locationLabel = `${indent}${keyChalk('Used at:'.padEnd(titleText.length, ' '))}`
624
+ if (variableInstances.length > 1) {
625
+ locationRender = `\n${variableInstances.map((v) => valueChalk(`${indent}- ${v.path}`)).join('\n')}`
626
+ const locationLabelText = `${indent}${keyChalk('Used at:')}`
627
+ locationLabel = locationLabelText
628
+ }
629
+
630
+ varMsg += `\n${locationLabel} ${locationRender}`
631
+
632
+ // console.log(` ${chalk.bold(key)}`)
633
+ console.log(makeBox(varMsg, {
634
+ title: `${key}`,
635
+ borderColor: 'gray',
636
+ // style: 'bold',
637
+ minWidth: 120,
638
+ }))
639
+ if(i < varKeys.length - 1) {
640
+ //console.log()
641
+ }
642
+ })
643
+ }
644
+
472
645
  /* Exit early if list or info flag is set */
473
646
  if (showFoundVariables) {
474
647
  process.exit(0)
@@ -571,7 +744,7 @@ class Configorama {
571
744
  this.config = mergeByKeys(this.config, '', this.mergeKeys)
572
745
  }
573
746
  if (VERBOSE) {
574
- console.log('───────────── Resolved Config ───────────────────')
747
+ logHeader('Resolved Configuration value')
575
748
  console.log()
576
749
  deepLog(this.config)
577
750
  console.log()
@@ -153,7 +153,9 @@ function preProcess(ymlStr = '') {
153
153
 
154
154
  /* If have yaml object and vars not wrapped in quotes, wrap them */
155
155
  if (ymlStr.match(KEY_OBJECT)) {
156
- const hasObjects = matchOutermostBraces(ymlStr)
156
+ const values = matchOutermostBraces(ymlStr)
157
+ // console.log('values', values)
158
+ const hasObjects = values.filter((x) => !x.match(/{{resolve:/))
157
159
  // console.log('hasObjects', hasObjects)
158
160
  if (hasObjects && hasObjects.length) {
159
161
  hasObjects.forEach((txt) => {
@@ -187,6 +189,15 @@ function preProcess(ymlStr = '') {
187
189
  }
188
190
  })
189
191
  }
192
+ // Automagically wrap CF https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references-ssm.html
193
+ const cfParams = values.filter((x) => !x.match(/\s/) && x.match(/{{resolve:/))
194
+ if (cfParams && cfParams.length) {
195
+ cfParams.forEach((txt) => {
196
+ const pat = new RegExp(`([^'"])${txt}([^'"])`, 'g')
197
+ const fixedText = `$1"${txt}"$2`
198
+ ymlStr = ymlStr.replace(pat, fixedText)
199
+ })
200
+ }
190
201
  }
191
202
  // console.log('ymlStr', ymlStr)
192
203
  return ymlStr
@@ -0,0 +1,9 @@
1
+ const safeChalk = require('safe-chalk')
2
+ const minimist = require('minimist')
3
+ const argv = minimist(process.argv.slice(2))
4
+
5
+ // If --json flag disable chalk colors
6
+ const DISABLE_COLORS = argv.json || process.env.NO_COLORS
7
+
8
+ // Export chalk instance for usage in CLI
9
+ module.exports = safeChalk(DISABLE_COLORS)
@@ -51,6 +51,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
51
51
 
52
52
  // Store match details
53
53
  const matchInfo = {
54
+ varType: undefined,
54
55
  location,
55
56
  value: input,
56
57
  fullMatch: match[0],
@@ -91,32 +92,33 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
91
92
  matches[i].varString = matches[i].variable
92
93
  /* Save additional meta data about the variable */
93
94
  // console.log('matches[i].varString', matches[i].varString)
94
- if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
95
- matches[i].varType = matches[i].varString.match(variablesKnownTypes)[1]
96
- if (FALLBACK_REGEX.test(matches[i].varString)) {
97
- const split = splitByComma(matches[i].varString, regex)
98
- matches[i].hasFallback = true
99
95
 
100
- matches[i].valueBeforeFallback = split[0]
101
- // remove first element from split
102
- matches[i].fallbackValues = split.slice(1).map((item) => {
103
- // console.log('item', item)
104
- const isVariable = variablesKnownTypes.test(item) || VAR_MATCH_REGEX.test(item)
105
- const fallbackData = {
106
- isVariable,
107
- fullMatch: item,
108
- variable: item
109
- }
96
+ // if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
97
+ // matches[i].varType = matches[i].varString.match(variablesKnownTypes)[1]
98
+ // if (FALLBACK_REGEX.test(matches[i].varString)) {
99
+ // const split = splitByComma(matches[i].varString, regex)
100
+ // matches[i].hasFallback = true
110
101
 
111
- if (!isVariable && typeof item === 'string') {
112
- fallbackData.stringValue = trimQuotes(item)
113
- fallbackData.isFallback = true
114
- }
102
+ // matches[i].valueBeforeFallback = split[0]
103
+ // // remove first element from split
104
+ // matches[i].fallbackValues = split.slice(1).map((item) => {
105
+ // // console.log('item', item)
106
+ // const isVariable = variablesKnownTypes.test(item) || VAR_MATCH_REGEX.test(item)
107
+ // const fallbackData = {
108
+ // isVariable,
109
+ // fullMatch: item,
110
+ // variable: item,
111
+ // }
112
+
113
+ // if (!isVariable && typeof item === 'string') {
114
+ // fallbackData.stringValue = trimQuotes(item)
115
+ // fallbackData.isResolvedFallback = true
116
+ // }
115
117
 
116
- return fallbackData
117
- })
118
- }
119
- }
118
+ // return fallbackData
119
+ // })
120
+ // }
121
+ // }
120
122
  }
121
123
 
122
124
  // Second pass: Reconstruct each variable with original nested syntax
@@ -161,9 +163,102 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
161
163
  // Reconstruct with all nested variables
162
164
  currentMatch.fullMatch = replaceAllPlaceholders(currentMatch.fullMatch, matches)
163
165
  currentMatch.variable = replaceAllPlaceholders(currentMatch.variable, matches)
166
+ }
164
167
 
165
-
168
+
169
+ // We need to store varString - the variable string with placeholders
170
+ for (let i = 0; i < matches.length; i++) {
171
+ matches[i].varString = matches[i].variable
172
+ /* Save additional meta data about the variable */
173
+ // console.log('matches[i].varString', matches[i].varString)
174
+
175
+ if (variablesKnownTypes && variablesKnownTypes.test(matches[i].varString)) {
176
+ matches[i].varType = matches[i].varString.match(variablesKnownTypes)[1]
177
+ if (FALLBACK_REGEX.test(matches[i].varString)) {
178
+ const split = splitByComma(matches[i].varString, regex)
179
+ matches[i].hasFallback = true
180
+
181
+ matches[i].valueBeforeFallback = split[0]
182
+ // remove first element from split
183
+ matches[i].fallbackValues = split.slice(1).map((item) => {
184
+ // console.log('item', item)
185
+ const isVariable = variablesKnownTypes.test(item) || VAR_MATCH_REGEX.test(item)
186
+ const fallbackData = {
187
+ isVariable,
188
+ fullMatch: item,
189
+ variable: item,
190
+ }
191
+
192
+ if (!isVariable && typeof item === 'string') {
193
+ fallbackData.stringValue = trimQuotes(item)
194
+ fallbackData.isResolvedFallback = true
195
+ }
196
+
197
+ return fallbackData
198
+ })
199
+ }
200
+ } else if (typeof matches[i].varType === 'undefined') {
201
+ matches[i].varType = 'dot.prop'
202
+ }
166
203
  }
204
+
205
+ const finalMatches = matches.map((m) => {
206
+ delete m.placeholder
207
+ if (typeof m.varType === 'undefined') {
208
+ /*
209
+ {
210
+ varType: 'dot.prop',
211
+ location: 'resolvedDomainName',
212
+ value: '${domainByStage.${opt:stage, ${defaultStage}}}',
213
+ fullMatch: '${domainByStage.${opt:stage, ${defaultStage}}}',
214
+ variable: 'domainByStage.${opt:stage, ${defaultStage}}',
215
+ varString: 'domainByStage.__VAR_1__',
216
+ resolveOrder: 3,
217
+ start: 0,
218
+ end: 26
219
+ }
220
+ {
221
+ varType: 'dot.prop',
222
+ location: 'resolvedDomainName',
223
+ value: '${domainByStage.${opt:stage, ${defaultStage}}}',
224
+ fullMatch: '${defaultStage}',
225
+ variable: 'defaultStage',
226
+ varString: 'defaultStage',
227
+ resolveOrder: 1,
228
+ start: 29,
229
+ end: 44
230
+ }
231
+ */
232
+ // console.log('m', m)
233
+ }
234
+ if (m.hasFallback) {
235
+ const combinedFallbacks = m.fallbackValues.reduce((acc, f) => {
236
+ const child = matches.find((m) => m.variable === f.variable)
237
+ if (child && child.fallbackValues && child.fallbackValues.length) {
238
+ const split = splitByComma(child.variable, regex)
239
+ f.valueBeforeFallback = split[0]
240
+ f.fallbackValues = child.fallbackValues
241
+ }
242
+ return acc
243
+ }, m.fallbackValues)
244
+ m.fallbackValues = combinedFallbacks
245
+ }
246
+ if (m.varType === 'dot.prop') {
247
+ // const reversedMatches = matches.reverse()
248
+ // const test = reversedMatches.reduce((acc, f) => {
249
+ // console.log('f', f)
250
+ // const child = reversedMatches.find((m) => m.variable === f.variable)
251
+ // if (child && child.fallbackValues && child.fallbackValues.length) {
252
+ // const split = splitByComma(child.variable, regex)
253
+ // f.valueBeforeFallback = split[0]
254
+ // f.fallbackValues = child.fallbackValues
255
+ // }
256
+ // return acc
257
+ // }, reversedMatches)
258
+ // console.log('test', test)
259
+ }
260
+ return m
261
+ })
167
262
 
168
263
  if (debug) {
169
264
  console.log("\nReconstructed matches:")
@@ -176,7 +271,7 @@ function findNestedVariables(input, regex, variablesKnownTypes, location, debug
176
271
  })
177
272
  }
178
273
 
179
- return matches
274
+ return finalMatches
180
275
  }
181
276
 
182
277
 
@@ -0,0 +1,15 @@
1
+ const { makeHeader } = require('@davidwells/box-logger')
2
+
3
+ function logHeader(message) {
4
+ console.log(makeHeader({
5
+ text: message,
6
+ rightBorder: true,
7
+ minWidth: 80,
8
+ textStyle: 'normal',
9
+ borderColor: 'cyanBright',
10
+ }))
11
+ }
12
+
13
+ module.exports = {
14
+ logHeader
15
+ }