configorama 0.6.19 → 0.7.1

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 (139) hide show
  1. package/README.md +78 -2
  2. package/cli.js +1 -0
  3. package/index.d.ts +90 -37
  4. package/package.json +8 -3
  5. package/src/index.js +42 -14
  6. package/src/main.js +72 -51
  7. package/src/parsers/index.js +10 -0
  8. package/src/parsers/typescript.js +20 -43
  9. package/src/parsers/yaml.js +35 -2
  10. package/src/resolvers/valueFromFile.js +87 -24
  11. package/src/resolvers/valueFromGit.js +11 -3
  12. package/src/utils/encoders/js-fixes.js +44 -0
  13. package/src/utils/parsing/mergeByKeys.js +4 -3
  14. package/src/utils/parsing/parse.js +4 -2
  15. package/src/utils/paths/getFullFilePath.js +1 -1
  16. package/src/utils/resolution/preResolveVariable.js +1 -0
  17. package/src/utils/strings/quoteUtils.js +2 -2
  18. package/src/utils/strings/splitCsv.js +1 -1
  19. package/src/utils/ui/logs.js +4 -4
  20. package/src/utils/validation/warnIfNotFound.js +3 -3
  21. package/src/utils/variables/findNestedVariables.js +2 -2
  22. package/types/cli.d.ts +3 -0
  23. package/types/cli.d.ts.map +1 -0
  24. package/types/src/functions/md5.d.ts +3 -0
  25. package/types/src/functions/md5.d.ts.map +1 -0
  26. package/types/src/index.d.ts +100 -0
  27. package/types/src/index.d.ts.map +1 -0
  28. package/types/src/main.d.ts +275 -0
  29. package/types/src/main.d.ts.map +1 -0
  30. package/types/src/parsers/esm.d.ts +15 -0
  31. package/types/src/parsers/esm.d.ts.map +1 -0
  32. package/types/src/parsers/hcl.d.ts +1 -0
  33. package/types/src/parsers/hcl.d.ts.map +1 -0
  34. package/types/src/parsers/index.d.ts +18 -0
  35. package/types/src/parsers/index.d.ts.map +1 -0
  36. package/types/src/parsers/ini.d.ts +6 -0
  37. package/types/src/parsers/ini.d.ts.map +1 -0
  38. package/types/src/parsers/json5.d.ts +10 -0
  39. package/types/src/parsers/json5.d.ts.map +1 -0
  40. package/types/src/parsers/toml.d.ts +7 -0
  41. package/types/src/parsers/toml.d.ts.map +1 -0
  42. package/types/src/parsers/typescript.d.ts +15 -0
  43. package/types/src/parsers/typescript.d.ts.map +1 -0
  44. package/types/src/parsers/yaml.d.ts +45 -0
  45. package/types/src/parsers/yaml.d.ts.map +1 -0
  46. package/types/src/resolvers/valueFromCron.d.ts +14 -0
  47. package/types/src/resolvers/valueFromCron.d.ts.map +1 -0
  48. package/types/src/resolvers/valueFromEnv.d.ts +8 -0
  49. package/types/src/resolvers/valueFromEnv.d.ts.map +1 -0
  50. package/types/src/resolvers/valueFromEval.d.ts +7 -0
  51. package/types/src/resolvers/valueFromEval.d.ts.map +1 -0
  52. package/types/src/resolvers/valueFromFile.d.ts +58 -0
  53. package/types/src/resolvers/valueFromFile.d.ts.map +1 -0
  54. package/types/src/resolvers/valueFromGit.d.ts +11 -0
  55. package/types/src/resolvers/valueFromGit.d.ts.map +1 -0
  56. package/types/src/resolvers/valueFromNumber.d.ts +6 -0
  57. package/types/src/resolvers/valueFromNumber.d.ts.map +1 -0
  58. package/types/src/resolvers/valueFromOptions.d.ts +9 -0
  59. package/types/src/resolvers/valueFromOptions.d.ts.map +1 -0
  60. package/types/src/resolvers/valueFromSelf.d.ts +1 -0
  61. package/types/src/resolvers/valueFromSelf.d.ts.map +1 -0
  62. package/types/src/resolvers/valueFromString.d.ts +6 -0
  63. package/types/src/resolvers/valueFromString.d.ts.map +1 -0
  64. package/types/src/sync.d.ts +3 -0
  65. package/types/src/sync.d.ts.map +1 -0
  66. package/types/src/utils/PromiseTracker.d.ts +19 -0
  67. package/types/src/utils/PromiseTracker.d.ts.map +1 -0
  68. package/types/src/utils/encoders/index.d.ts +2 -0
  69. package/types/src/utils/encoders/index.d.ts.map +1 -0
  70. package/types/src/utils/encoders/js-fixes.d.ts +23 -0
  71. package/types/src/utils/encoders/js-fixes.d.ts.map +1 -0
  72. package/types/src/utils/encoders/unknown-values.d.ts +18 -0
  73. package/types/src/utils/encoders/unknown-values.d.ts.map +1 -0
  74. package/types/src/utils/handleSignalEvents.d.ts +3 -0
  75. package/types/src/utils/handleSignalEvents.d.ts.map +1 -0
  76. package/types/src/utils/lodash.d.ts +4 -0
  77. package/types/src/utils/lodash.d.ts.map +1 -0
  78. package/types/src/utils/parsing/arrayToJsonPath.d.ts +5 -0
  79. package/types/src/utils/parsing/arrayToJsonPath.d.ts.map +1 -0
  80. package/types/src/utils/parsing/cloudformationSchema.d.ts +2 -0
  81. package/types/src/utils/parsing/cloudformationSchema.d.ts.map +1 -0
  82. package/types/src/utils/parsing/enrichMetadata.d.ts +17 -0
  83. package/types/src/utils/parsing/enrichMetadata.d.ts.map +1 -0
  84. package/types/src/utils/parsing/mergeByKeys.d.ts +5 -0
  85. package/types/src/utils/parsing/mergeByKeys.d.ts.map +1 -0
  86. package/types/src/utils/parsing/parse.d.ts +54 -0
  87. package/types/src/utils/parsing/parse.d.ts.map +1 -0
  88. package/types/src/utils/parsing/preProcess.d.ts +10 -0
  89. package/types/src/utils/parsing/preProcess.d.ts.map +1 -0
  90. package/types/src/utils/paths/filePathUtils.d.ts +32 -0
  91. package/types/src/utils/paths/filePathUtils.d.ts.map +1 -0
  92. package/types/src/utils/paths/findLineForKey.d.ts +12 -0
  93. package/types/src/utils/paths/findLineForKey.d.ts.map +1 -0
  94. package/types/src/utils/paths/findProjectRoot.d.ts +2 -0
  95. package/types/src/utils/paths/findProjectRoot.d.ts.map +1 -0
  96. package/types/src/utils/paths/getFullFilePath.d.ts +25 -0
  97. package/types/src/utils/paths/getFullFilePath.d.ts.map +1 -0
  98. package/types/src/utils/paths/resolveAlias.d.ts +14 -0
  99. package/types/src/utils/paths/resolveAlias.d.ts.map +1 -0
  100. package/types/src/utils/regex/index.d.ts +14 -0
  101. package/types/src/utils/regex/index.d.ts.map +1 -0
  102. package/types/src/utils/resolution/preResolveVariable.d.ts +51 -0
  103. package/types/src/utils/resolution/preResolveVariable.d.ts.map +1 -0
  104. package/types/src/utils/strings/bracketMatcher.d.ts +25 -0
  105. package/types/src/utils/strings/bracketMatcher.d.ts.map +1 -0
  106. package/types/src/utils/strings/formatFunctionArgs.d.ts +3 -0
  107. package/types/src/utils/strings/formatFunctionArgs.d.ts.map +1 -0
  108. package/types/src/utils/strings/quoteUtils.d.ts +31 -0
  109. package/types/src/utils/strings/quoteUtils.d.ts.map +1 -0
  110. package/types/src/utils/strings/replaceAll.d.ts +9 -0
  111. package/types/src/utils/strings/replaceAll.d.ts.map +1 -0
  112. package/types/src/utils/strings/splitByComma.d.ts +2 -0
  113. package/types/src/utils/strings/splitByComma.d.ts.map +1 -0
  114. package/types/src/utils/strings/splitCsv.d.ts +10 -0
  115. package/types/src/utils/strings/splitCsv.d.ts.map +1 -0
  116. package/types/src/utils/strings/textUtils.d.ts +15 -0
  117. package/types/src/utils/strings/textUtils.d.ts.map +1 -0
  118. package/types/src/utils/ui/chalk.d.ts +70 -0
  119. package/types/src/utils/ui/chalk.d.ts.map +1 -0
  120. package/types/src/utils/ui/configWizard.d.ts +67 -0
  121. package/types/src/utils/ui/configWizard.d.ts.map +1 -0
  122. package/types/src/utils/ui/createEditorLink.d.ts +11 -0
  123. package/types/src/utils/ui/createEditorLink.d.ts.map +1 -0
  124. package/types/src/utils/ui/deep-log.d.ts +3 -0
  125. package/types/src/utils/ui/deep-log.d.ts.map +1 -0
  126. package/types/src/utils/ui/logs.d.ts +2 -0
  127. package/types/src/utils/ui/logs.d.ts.map +1 -0
  128. package/types/src/utils/validation/warnIfNotFound.d.ts +15 -0
  129. package/types/src/utils/validation/warnIfNotFound.d.ts.map +1 -0
  130. package/types/src/utils/variables/appendDeepVariable.d.ts +3 -0
  131. package/types/src/utils/variables/appendDeepVariable.d.ts.map +1 -0
  132. package/types/src/utils/variables/cleanVariable.d.ts +3 -0
  133. package/types/src/utils/variables/cleanVariable.d.ts.map +1 -0
  134. package/types/src/utils/variables/findNestedVariables.d.ts +23 -0
  135. package/types/src/utils/variables/findNestedVariables.d.ts.map +1 -0
  136. package/types/src/utils/variables/getVariableType.d.ts +8 -0
  137. package/types/src/utils/variables/getVariableType.d.ts.map +1 -0
  138. package/types/src/utils/variables/variableUtils.d.ts +21 -0
  139. package/types/src/utils/variables/variableUtils.d.ts.map +1 -0
package/src/main.js CHANGED
@@ -27,7 +27,7 @@ const handleSignalEvents = require('./utils/handleSignalEvents')
27
27
  /* Utils - encoders */
28
28
  const { encodeUnknown, decodeUnknown } = require('./utils/encoders/unknown-values')
29
29
  const { decodeEncodedValue } = require('./utils/encoders')
30
- const { encodeJsSyntax, decodeJsSyntax, hasParenthesesPlaceholder } = require('./utils/encoders/js-fixes')
30
+ const { encodeJsSyntax, decodeJsSyntax, hasParenthesesPlaceholder, encodeJsonForVariable } = require('./utils/encoders/js-fixes')
31
31
 
32
32
  /* Utils - parsing */
33
33
  const enrichMetadata = require('./utils/parsing/enrichMetadata')
@@ -106,8 +106,8 @@ const deepRefSyntax = RegExp(/(\${)?deep:\d+(\.[^}]+)*()}?/)
106
106
  const deepIndexReplacePattern = new RegExp(/^deep:|(\.[^}]+)*$/g)
107
107
  const deepIndexPattern = /deep\:(\d*)/
108
108
  const deepPrefixReplacePattern = /(?:^deep:)\d+\.?/g
109
- const fileRefSyntax = RegExp(/^file\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)/g)
110
- const textRefSyntax = RegExp(/^text\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" ]+?)\)/g)
109
+ const fileRefSyntax = RegExp(/^file\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" =+]+?)\)/g)
110
+ const textRefSyntax = RegExp(/^text\((~?[@\{\}\:\$a-zA-Z0-9._\-\/,'" =+]+?)\)/g)
111
111
  // TODO update file regex ^file\((~?[a-zA-Z0-9._\-\/, ]+?)\)
112
112
  // To match file(asyncValue.js, lol) input params
113
113
  const envRefSyntax = RegExp(/^env:/g)
@@ -132,7 +132,7 @@ class Configorama {
132
132
 
133
133
  const options = opts || {}
134
134
  // Set opts to pass into JS file calls
135
- this.opts = Object.assign({}, {
135
+ this.settings = Object.assign({}, {
136
136
  // Allow for unknown variable syntax to pass through without throwing errors
137
137
  allowUnknownVariables: false,
138
138
  // Allow undefined to be an end result.
@@ -149,7 +149,7 @@ class Configorama {
149
149
 
150
150
  // Backward compat: allowUnknownVars -> allowUnknownVariables
151
151
  if (options.allowUnknownVars !== undefined && options.allowUnknownVariables === undefined) {
152
- this.opts.allowUnknownVariables = options.allowUnknownVars
152
+ this.settings.allowUnknownVariables = options.allowUnknownVars
153
153
  }
154
154
 
155
155
  this.filterCache = {}
@@ -375,11 +375,13 @@ class Configorama {
375
375
  }
376
376
 
377
377
  /* attach self matcher last */
378
- this.variableTypes = this.variableTypes.concat(fallThroughSelfMatcher)
378
+ this.variableTypes = this.variableTypes.concat(/** @type {any} */ (fallThroughSelfMatcher))
379
379
 
380
380
  // const variablesKnownTypes = new RegExp(`^(${this.variableTypes.map((v) => v.prefix || v.type).join('|')}):`)
381
381
  const variablesKnownTypes = combineRegexes(
382
- this.variableTypes.filter((v) => v.type !== 'string').map((v) => v.match)
382
+ /** @type {RegExp[]} */ (this.variableTypes
383
+ .filter((v) => v.type !== 'string' && v.match instanceof RegExp)
384
+ .map((v) => v.match))
383
385
  )
384
386
  this.variablesKnownTypes = variablesKnownTypes
385
387
 
@@ -547,11 +549,11 @@ class Configorama {
547
549
  * Populate all variables in the service, conveniently remove and restore the service attributes
548
550
  * that confuse the population methods.
549
551
  * @param cliOpts An options hive to use for ${opt:...} variables.
550
- * @returns {Promise.<TResult>|*} A promise resolving to the populated service.
552
+ * @returns {Promise<any>} A promise resolving to the populated service.
551
553
  */
552
554
  async init(cliOpts) {
553
555
  this.options = cliOpts || {}
554
- const configoramaOpts = this.opts
556
+ const configoramaOpts = this.settings
555
557
 
556
558
  const showFoundVariables = configoramaOpts && configoramaOpts.dynamicArgs && (configoramaOpts.dynamicArgs.list || configoramaOpts.dynamicArgs.info)
557
559
 
@@ -562,10 +564,10 @@ class Configorama {
562
564
  contents: this.originalString,
563
565
  filePath: this.configFilePath,
564
566
  varRegex: this.variableSyntax,
565
- dynamicArgs: this.opts.dynamicArgs
567
+ dynamicArgs: this.settings.dynamicArgs
566
568
  })
567
569
  this.configFileContents = ''
568
- if (VERBOSE || showFoundVariables || this.opts.returnPreResolvedVariableDetails || SETUP_MODE) {
570
+ if (VERBOSE || showFoundVariables || this.settings.returnPreResolvedVariableDetails || SETUP_MODE) {
569
571
  this.configFileContents = fs.readFileSync(this.configFilePath, 'utf8')
570
572
  }
571
573
  /*
@@ -595,7 +597,7 @@ class Configorama {
595
597
  const variableSyntax = this.variableSyntax
596
598
  const variablesKnownTypes = this.variablesKnownTypes
597
599
 
598
- if (VERBOSE || showFoundVariables || this.opts.returnPreResolvedVariableDetails || SETUP_MODE) {
600
+ if (VERBOSE || showFoundVariables || this.settings.returnPreResolvedVariableDetails || SETUP_MODE) {
599
601
  const metadata = this.collectVariableMetadata()
600
602
 
601
603
  const enrich = await enrichMetadata(
@@ -607,7 +609,7 @@ class Configorama {
607
609
  this.configFilePath,
608
610
  Object.keys(this.filters),
609
611
  undefined, // resolvedConfig not available yet
610
- this.opts.options,
612
+ this.settings.options,
611
613
  this.variableTypes
612
614
  )
613
615
 
@@ -625,7 +627,7 @@ class Configorama {
625
627
  const varKeys = Object.keys(variableData)
626
628
  const uniqueVarKeys = Object.keys(uniqueVariables)
627
629
 
628
- if (this.opts.returnPreResolvedVariableDetails) {
630
+ if (this.settings.returnPreResolvedVariableDetails) {
629
631
  return Object.assign({}, {
630
632
  resolved: false,
631
633
  originalConfig: this.originalConfig
@@ -644,7 +646,7 @@ class Configorama {
644
646
  if (varTypes.length) {
645
647
  const exclude = ['dot.prop', 'deep']
646
648
  console.log('\nAllowed variable types:')
647
- varTypes.filter((v) => v.type !== 'dot.prop').forEach((v) => {
649
+ varTypes.forEach((v) => {
648
650
  const vData = this.variableTypes[v]
649
651
  if (exclude.includes(vData.type)) {
650
652
  return
@@ -1141,7 +1143,7 @@ class Configorama {
1141
1143
  }
1142
1144
 
1143
1145
  const useDotEnv = this.originalConfig.useDotenv || this.originalConfig.useDotEnv
1144
- if ((useDotEnv && useDotEnv === true) || this.opts.useDotEnvFiles) {
1146
+ if ((useDotEnv && useDotEnv === true) || this.settings.useDotEnvFiles) {
1145
1147
  let providerStage
1146
1148
  /* has hardcoded stage */
1147
1149
  if (
@@ -1244,8 +1246,8 @@ class Configorama {
1244
1246
  .then(() => {
1245
1247
  // console.log('this.config', this.config)
1246
1248
  /* Final post-processing here */
1247
- if (this.mergeKeys && this.config) {
1248
- this.config = mergeByKeys(this.config, '', this.mergeKeys)
1249
+ if (this.settings.mergeKeys && this.config) {
1250
+ this.config = mergeByKeys(this.config, '', this.settings.mergeKeys)
1249
1251
  }
1250
1252
  if (VERBOSE) {
1251
1253
  logHeader('Resolved Configuration value')
@@ -1409,6 +1411,7 @@ class Configorama {
1409
1411
  })
1410
1412
 
1411
1413
  const varData = {
1414
+ filters: foundFilters.length > 0 ? foundFilters : undefined,
1412
1415
  path: configValuePath,
1413
1416
  key: itemKey,
1414
1417
  originalStringValue: rawValue,
@@ -1416,10 +1419,12 @@ class Configorama {
1416
1419
  variableWithFilters: originalSrc,
1417
1420
  isRequired: false,
1418
1421
  defaultValue: undefined,
1422
+ defaultValueIsVar: undefined,
1423
+ defaultValueSrc: undefined,
1424
+ hasFallback: false,
1419
1425
  matchIndex: matchCount++,
1420
1426
  resolveOrder: [],
1421
1427
  resolveDetails: cleanedResolveDetails,
1422
- ...(foundFilters.length > 0 && { filters: foundFilters }),
1423
1428
  }
1424
1429
  let defaultValueIsVar = false
1425
1430
 
@@ -1691,7 +1696,7 @@ class Configorama {
1691
1696
  /**
1692
1697
  * Populate the variables in the given object.
1693
1698
  * @param objectToPopulate The object to populate variables within.
1694
- * @returns {Promise.<TResult>|*} A promise resolving to the in-place populated object.
1699
+ * @returns {Promise<any>} A promise resolving to the in-place populated object.
1695
1700
  */
1696
1701
  populateObject(objectToPopulate) {
1697
1702
  return this.initialCall(() => this.populateObjectImpl(objectToPopulate))
@@ -1741,7 +1746,7 @@ class Configorama {
1741
1746
  * ]
1742
1747
  * @typedef {Object} TerminalProperty
1743
1748
  * @property {String[]} path The path to the terminal property
1744
- * @property {Date|RegEx|String} The value of the terminal property
1749
+ * @property {Date|RegExp|String} value The value of the terminal property
1745
1750
  */
1746
1751
  /**
1747
1752
  * Generate an array of objects noting the terminal properties of the given root object and their
@@ -1857,8 +1862,7 @@ class Configorama {
1857
1862
  * Assign the populated values back to the target object
1858
1863
  * @param target The object to which the given populated terminal properties should be applied
1859
1864
  * @param populations The fully populated terminal properties
1860
- * @returns {Promise<number>} resolving with the number of changes that were applied to the given
1861
- * target
1865
+ * @returns {Promise<void>} resolving when changes have been applied to the given target
1862
1866
  */
1863
1867
  assignProperties(target, populations) {
1864
1868
  // eslint-disable-line class-methods-use-this
@@ -2098,7 +2102,7 @@ class Configorama {
2098
2102
  * @param valueObject The value to populate variables within
2099
2103
  * @param root Whether the caller is the root populater and thereby whether to recursively
2100
2104
  * populate
2101
- * @returns {PromiseLike<T>} A promise that resolves to the populated value, recursively if root
2105
+ * @returns {Promise<any>} A promise that resolves to the populated value, recursively if root
2102
2106
  * is true
2103
2107
  */
2104
2108
  populateValue(valueObject, root, caller) {
@@ -2181,11 +2185,14 @@ class Configorama {
2181
2185
  /**
2182
2186
  * Populate a given property, given the matched string to replace and the value to replace the
2183
2187
  * matched string with.
2184
- * @param valueObject.value The property to replace the matched string with the value.
2188
+ * @param {object} valueObject The value object containing the property to populate
2189
+ * @param {any} valueObject.value The property to replace the matched string with the value.
2190
+ * @param {string[]} [valueObject.path] The path to the value in the config.
2191
+ * @param {string} [valueObject.originalSource] The original source string.
2192
+ * @param {Array} [valueObject.resolutionHistory] History of resolution steps.
2185
2193
  * @param matchedString The string in the given property that was matched and is to be replaced.
2186
2194
  * @param valueToPopulate The value to replace the given matched string in the property with.
2187
- * @returns {Promise.<TResult>|*} A promise resolving to the property populated with the given
2188
- * value for all instances of the given matched string.
2195
+ * @returns {{value: any, path?: string[], originalSource?: string, resolutionHistory?: Array, __internal_only_flag?: boolean, caller?: string, count?: number}} The populated property object
2189
2196
  */
2190
2197
  populateVariable(valueObject, matchedString, valueToPopulate) {
2191
2198
  let property = valueObject.value
@@ -2324,23 +2331,30 @@ class Configorama {
2324
2331
 
2325
2332
  const objStr = JSON.stringify(valueToPopulate)
2326
2333
  /* Check if variable inside another variable. E.g. ${env:${self:someObject}} that resolves to ${env:{...}} */
2327
- if (
2334
+ const isNestedInVariable = (
2328
2335
  property.trim() !== matchedString.trim() &&
2329
2336
  property.indexOf(matchedString) !== -1 &&
2330
2337
  matchedString.match(this.variableSyntax) &&
2331
2338
  property.match(this.variableSyntax)
2332
- ) {
2339
+ )
2340
+ // Only encode for file() or text() references where JSON braces break regex matching
2341
+ const isFileOrTextRef = /\bfile\s*\(|\btext\s*\(/.test(property)
2342
+ if (isNestedInVariable && isFileOrTextRef) {
2343
+ // Encode object as base64 to avoid breaking variable syntax with nested braces
2344
+ const encodedObj = encodeJsonForVariable(valueToPopulate)
2345
+ property = replaceAll(matchedString, encodedObj, property)
2346
+ } else if (isNestedInVariable) {
2333
2347
  const isVar = /^\${[a-zA-Z0-9_]+:/.test(property)
2334
2348
  if (isVar) {
2335
- // console.log('INSIDE', property, matchedString)
2336
- // console.log('isVar', isVar)
2337
2349
  throw new Error(
2338
2350
  `Invalid variable syntax "${property}" resolves to "${replaceAll(matchedString, objStr, property)}"`,
2339
2351
  )
2340
2352
  }
2353
+ property = replaceAll(matchedString, objStr, property)
2354
+ } else {
2355
+ // console.log('OBJECT MATCH', `"${objStr}"`)
2356
+ property = replaceAll(matchedString, objStr, property)
2341
2357
  }
2342
- // console.log('OBJECT MATCH', `"${objStr}"`)
2343
- property = replaceAll(matchedString, objStr, property) // .replace(/}$/, '').replace(/^\$\{/, '')
2344
2358
  // console.log('property', property)
2345
2359
  // TODO run functions here
2346
2360
  // console.log('other new prop', property)
@@ -2366,7 +2380,7 @@ class Configorama {
2366
2380
 
2367
2381
  if (nestedVar) {
2368
2382
  const fallbackStr = getFallbackString(splitVars, nestedVar)
2369
- if (!this.opts.allowUnknownVariables) {
2383
+ if (!this.settings.allowUnknownVariables) {
2370
2384
  verifyVariable(nestedVar, valueObject, this.variableTypes, this.config)
2371
2385
  }
2372
2386
 
@@ -2382,18 +2396,17 @@ class Configorama {
2382
2396
  }
2383
2397
 
2384
2398
  // If allowUnresolvedVariables and there are fallbacks, use the fallback
2385
- if (this.opts.allowUnresolvedVariables && splitVars.length > 1) {
2399
+ if (this.settings.allowUnresolvedVariables && splitVars.length > 1) {
2386
2400
  const nextFallback = splitVars[1].trim()
2387
2401
  // Strip trailing variable suffix (handles }, }}, >, ]], etc.)
2388
2402
  const nextFallbackClean = nextFallback.replace(this.varSuffixPattern, '')
2389
2403
  const isQuotedString = /^['"].*['"]$/.test(nextFallbackClean)
2390
2404
  const isNumeric = /^-?\d+(\.\d+)?$/.test(nextFallbackClean)
2391
2405
  if (isQuotedString || isNumeric) {
2392
- let staticValue = nextFallbackClean.replace(/^['"]|['"]$/g, '')
2406
+ const strValue = nextFallbackClean.replace(/^['"]|['"]$/g, '')
2393
2407
  // Convert to number if it's a numeric fallback
2394
- if (isNumeric) {
2395
- staticValue = Number(staticValue)
2396
- }
2408
+ /** @type {string|number} */
2409
+ const staticValue = isNumeric ? Number(strValue) : strValue
2397
2410
  return {
2398
2411
  value: staticValue,
2399
2412
  path: valueObject.path,
@@ -2484,7 +2497,7 @@ Missing Value ${missingValue} - ${matchedString}
2484
2497
  !prop.match(this.variableSyntax)
2485
2498
  &&
2486
2499
  /* Not file or text refs */
2487
- !prop.match(fileRefSyntax)
2500
+ !prop.match(fileRefSyntax)
2488
2501
  && !prop.match(textRefSyntax)
2489
2502
  /* Not eval refs */
2490
2503
  && !prop.match(getValueFromEval.match)
@@ -2573,8 +2586,10 @@ Missing Value ${missingValue} - ${matchedString}
2573
2586
  /**
2574
2587
  * Resolve the given variable string that expresses a series of fallback values in case the
2575
2588
  * initial values are not valid, resolving each variable and resolving to the first valid value.
2576
- * @param variableStringsString The overwrite string of variables to populate and choose from.
2577
- * @returns {Promise.<TResult>|*} A promise resolving to the first validly populating variable
2589
+ * @param variableStrings The overwrite string of variables to populate and choose from.
2590
+ * @param valueObject The value object
2591
+ * @param originalVar The original variable string
2592
+ * @returns {Promise<any>} A promise resolving to the first validly populating variable
2578
2593
  * in the given variable strings string.
2579
2594
  */
2580
2595
  overwrite(variableStrings, valueObject, originalVar) {
@@ -2650,7 +2665,10 @@ Missing Value ${missingValue} - ${matchedString}
2650
2665
  /**
2651
2666
  * Given any variable string, return the value it should be populated with.
2652
2667
  * @param variableString The variable string to retrieve a value for.
2653
- * @returns {Promise.<TResult>|*} A promise resolving to the given variables value.
2668
+ * @param valueObject The value object
2669
+ * @param caller The caller name
2670
+ * @param originalVar The original variable string
2671
+ * @returns {Promise<any>} A promise resolving to the given variables value.
2654
2672
  */
2655
2673
  getValueFromSource(variableString, valueObject, caller, originalVar) {
2656
2674
  // console.log('getValueFromSrc caller', caller)
@@ -2749,6 +2767,7 @@ Missing Value ${missingValue} - ${matchedString}
2749
2767
  variableString = trim(t[0])
2750
2768
  }
2751
2769
 
2770
+ /** @type {Function|undefined} */
2752
2771
  let resolverFunction
2753
2772
  let resolverType
2754
2773
  /* Loop over variables and set getterFunction when match found. */
@@ -2845,12 +2864,12 @@ Missing Value ${missingValue} - ${matchedString}
2845
2864
  // console.log('nestedVars', nestedVars)
2846
2865
  const noNestedVars = nestedVars.length < 2
2847
2866
 
2848
- if (this.opts.allowUnknownFileRefs && variableString.match(fileRefSyntax)) {
2867
+ if (this.settings.allowUnknownFileRefs && variableString.match(fileRefSyntax)) {
2849
2868
  // Encode the unknown file variable to pass through resolution
2850
2869
  return Promise.resolve(encodeUnknown(propertyString))
2851
2870
  }
2852
2871
 
2853
- if (this.opts.allowUnresolvedVariables) {
2872
+ if (this.settings.allowUnresolvedVariables) {
2854
2873
  // Check if outer expression has fallbacks we can use
2855
2874
  // valueCount[0] is the primary var, valueCount[1+] are fallbacks
2856
2875
  if (valueCount.length > 1) {
@@ -3027,7 +3046,7 @@ Missing Value ${missingValue} - ${matchedString}
3027
3046
  // console.log('nestedVar', nestedVar)
3028
3047
 
3029
3048
  if (nestedVar) {
3030
- if (!this.opts.allowUnknownVariables) {
3049
+ if (!this.settings.allowUnknownVariables) {
3031
3050
  verifyVariable(nestedVar, valueObject, this.variableTypes, this.config)
3032
3051
  }
3033
3052
  const fallbackStr = getFallbackString(split, nestedVar)
@@ -3105,7 +3124,7 @@ Missing Value ${missingValue} - ${matchedString}
3105
3124
  let allowSpecialCase = false
3106
3125
  /* handle special cases for cloudformation ${Sub} values */
3107
3126
  if (this.originalConfig && key.endsWith('Fn::Sub')) {
3108
- if (this.opts.verifySubReferences) {
3127
+ if (this.settings.verifySubReferences) {
3109
3128
  const params = this.originalConfig.Parameters || (this.originalConfig.resources || {}).Parameters
3110
3129
  const resources = this.originalConfig.Resources || (this.originalConfig.resources || {}).Resources
3111
3130
  /* Cloudformation Resource References */
@@ -3130,7 +3149,7 @@ Missing Value ${missingValue} - ${matchedString}
3130
3149
 
3131
3150
 
3132
3151
  /* Pass through unknown variables */
3133
- if (this.opts.allowUnknownVariables || allowSpecialCase) {
3152
+ if (this.settings.allowUnknownVariables || allowSpecialCase) {
3134
3153
  // console.log('allowUnknownVars propertyString', propertyString)
3135
3154
  const varMatches = propertyString.match(this.variableSyntax)
3136
3155
  let allowUnknownVars = propertyString
@@ -3188,7 +3207,7 @@ Missing Value ${missingValue} - ${matchedString}
3188
3207
  variableSyntax: this.variableSyntax,
3189
3208
  variablesKnownTypes: this.variablesKnownTypes,
3190
3209
  variableTypes: this.variableTypes,
3191
- opts: this.opts,
3210
+ opts: this.settings,
3192
3211
  originalConfig: this.originalConfig,
3193
3212
  config: this.config,
3194
3213
  getDeeperValue: this.getDeeperValue.bind(this),
@@ -3346,7 +3365,7 @@ Missing Value ${missingValue} - ${matchedString}
3346
3365
  })
3347
3366
  }
3348
3367
  runFunction(variableString) {
3349
- // console.log('runFunction', variableString)
3368
+ console.log('runFunction', variableString)
3350
3369
  /* If json object value return it */
3351
3370
  if (variableString.match(/^\s*{/) && variableString.match(/}\s*$/)) {
3352
3371
  return variableString
@@ -3378,7 +3397,9 @@ Missing Value ${missingValue} - ${matchedString}
3378
3397
  // TODO check for camelCase version. | toUpperCase messes with function name
3379
3398
  const theFunction = this.functions[functionName] || this.functions[functionName.toLowerCase()]
3380
3399
 
3381
- if (!theFunction) throw new Error(`Function "${functionName}" not found`)
3400
+ if (!theFunction) {
3401
+ throw new Error(`Function "${functionName}" not found`)
3402
+ }
3382
3403
 
3383
3404
  const funcValue = theFunction(...argsToPass)
3384
3405
  // console.log('funcValue', funcValue)
@@ -1,8 +1,18 @@
1
+ /**
2
+ * @typedef {Object} ParserFunction
3
+ * @property {Function} parse - Parse string content into object
4
+ * @property {Function} stringify - Convert object to string format
5
+ */
6
+
1
7
  const json = require('./json5')
2
8
  const toml = require('./toml')
3
9
  const yaml = require('./yaml')
4
10
  const ini = require('./ini')
5
11
 
12
+ /**
13
+ * Collection of format parsers for different config file types
14
+ * @type {Object.<string, ParserFunction>}
15
+ */
6
16
  module.exports = {
7
17
  json: json,
8
18
  toml: toml,
@@ -2,10 +2,10 @@ const path = require('path')
2
2
  const fs = require('fs')
3
3
 
4
4
  /**
5
- * Execute TypeScript file and return its export
5
+ * Load TypeScript file and return its export (without executing)
6
6
  * @param {string} filePath - Full path to the TypeScript file
7
- * @param {Object} opts - Additional options including dynamicArgs
8
- * @returns {Promise<*>} The result of executing the TypeScript file
7
+ * @param {Object} opts - Additional options (unused, kept for API compat)
8
+ * @returns {Promise<*>} The exported module from the TypeScript file
9
9
  */
10
10
  async function executeTypeScriptFile(filePath, opts = {}) {
11
11
  // Check if tsx is available first (preferred)
@@ -34,6 +34,7 @@ async function executeTypeScriptFile(filePath, opts = {}) {
34
34
  let tsFile
35
35
  if (useTsx) {
36
36
  // Use tsx for modern, fast TypeScript execution
37
+ // @ts-ignore - tsx doesn't have type declarations
37
38
  const { register } = require('tsx/cjs/api')
38
39
  const restore = register()
39
40
  try {
@@ -46,6 +47,7 @@ async function executeTypeScriptFile(filePath, opts = {}) {
46
47
  } else {
47
48
  // Fallback to ts-node
48
49
  try {
50
+ // @ts-ignore - ts-node is optional peer dependency
49
51
  require('ts-node/register')
50
52
  tsFile = require(filePath)
51
53
  } catch (err) {
@@ -53,34 +55,19 @@ async function executeTypeScriptFile(filePath, opts = {}) {
53
55
  }
54
56
  }
55
57
 
56
- if (typeof tsFile !== 'function') {
57
- return tsFile
58
- } else {
59
- let tsArgs = opts.dynamicArgs || {}
60
- if (tsArgs && typeof tsArgs === 'function') {
61
- tsArgs = tsArgs()
62
- }
63
-
64
- try {
65
- const result = tsFile(tsArgs)
66
-
67
- // Handle promises
68
- if (result && typeof result.then === 'function') {
69
- return await result
70
- }
71
-
72
- return result
73
- } catch (err) {
74
- throw new Error(`Error executing TypeScript function: ${err.message}`)
75
- }
58
+ // Handle ES module default exports
59
+ if (tsFile && typeof tsFile === 'object' && 'default' in tsFile) {
60
+ tsFile = tsFile.default
76
61
  }
62
+
63
+ return tsFile
77
64
  }
78
65
 
79
66
  /**
80
- * Synchronous TypeScript file execution (using tsx with sync API)
67
+ * Load TypeScript file synchronously and return its export
81
68
  * @param {string} filePath - Full path to the TypeScript file
82
- * @param {Object} opts - Additional options including dynamicArgs
83
- * @returns {*} The result of executing the TypeScript file
69
+ * @param {Object} opts - Additional options (unused, kept for API compat)
70
+ * @returns {*} The exported module from the TypeScript file
84
71
  */
85
72
  function executeTypeScriptFileSync(filePath, opts = {}) {
86
73
  // Check if tsx is available first (preferred)
@@ -109,6 +96,7 @@ function executeTypeScriptFileSync(filePath, opts = {}) {
109
96
  let tsFile
110
97
  if (useTsx) {
111
98
  // Use tsx for modern, fast TypeScript execution
99
+ // @ts-ignore - tsx doesn't have type declarations
112
100
  const { register } = require('tsx/cjs/api')
113
101
  const restore = register()
114
102
  try {
@@ -121,6 +109,7 @@ function executeTypeScriptFileSync(filePath, opts = {}) {
121
109
  } else {
122
110
  // Fallback to ts-node
123
111
  try {
112
+ // @ts-ignore - ts-node is optional peer dependency
124
113
  require('ts-node/register')
125
114
  tsFile = require(filePath)
126
115
  } catch (err) {
@@ -128,24 +117,12 @@ function executeTypeScriptFileSync(filePath, opts = {}) {
128
117
  }
129
118
  }
130
119
 
131
- if (typeof tsFile !== 'function') {
132
- return tsFile
133
- } else {
134
- let tsArgs = opts.dynamicArgs || {}
135
- if (tsArgs && typeof tsArgs === 'function') {
136
- tsArgs = tsArgs()
137
- }
138
-
139
- try {
140
- const result = tsFile(tsArgs)
141
-
142
- // Note: For sync execution, we don't await promises
143
- // If the function returns a promise, it will be resolved by the calling code
144
- return result
145
- } catch (err) {
146
- throw new Error(`Error executing TypeScript function: ${err.message}`)
147
- }
120
+ // Handle ES module default exports
121
+ if (tsFile && typeof tsFile === 'object' && 'default' in tsFile) {
122
+ tsFile = tsFile.default
148
123
  }
124
+
125
+ return tsFile
149
126
  }
150
127
 
151
128
  module.exports = {
@@ -3,7 +3,12 @@ const TOML = require('./toml')
3
3
  const JSON = require('./json5')
4
4
  const { findOutermostVariables, findOutermostBracesDepthFirst } = require('../utils/strings/bracketMatcher')
5
5
 
6
- // Loader for custom CF syntax
6
+ /**
7
+ * Loader for custom CF syntax
8
+ * @param {string|Buffer} contents - YAML content to load
9
+ * @param {Object} [options] - YAML load options
10
+ * @returns {{data: Object|null, error: Error|null}} Parsed data and error if any
11
+ */
7
12
  function load(contents, options) {
8
13
  let data
9
14
  let error
@@ -15,6 +20,12 @@ function load(contents, options) {
15
20
  return { data, error }
16
21
  }
17
22
 
23
+ /**
24
+ * Parse YAML content into JavaScript object
25
+ * @param {string} ymlContents - YAML string to parse
26
+ * @returns {Object} Parsed YAML object
27
+ * @throws {Error} If YAML parsing fails
28
+ */
18
29
  function parse(ymlContents) {
19
30
  // Get document, or throw exception on error
20
31
  let ymlObject = {}
@@ -26,6 +37,12 @@ function parse(ymlContents) {
26
37
  return ymlObject
27
38
  }
28
39
 
40
+ /**
41
+ * Convert JavaScript object to YAML string
42
+ * @param {Object} object - Object to convert to YAML
43
+ * @returns {string} YAML string representation
44
+ * @throws {Error} If conversion fails
45
+ */
29
46
  function dump(object) {
30
47
  let yml
31
48
  try {
@@ -38,6 +55,12 @@ function dump(object) {
38
55
  return yml
39
56
  }
40
57
 
58
+ /**
59
+ * Convert YAML content to TOML format
60
+ * @param {string} ymlContents - YAML string to convert
61
+ * @returns {string} TOML string representation
62
+ * @throws {Error} If conversion fails
63
+ */
41
64
  function toToml(ymlContents) {
42
65
  let toml
43
66
  try {
@@ -48,6 +71,12 @@ function toToml(ymlContents) {
48
71
  return toml
49
72
  }
50
73
 
74
+ /**
75
+ * Convert YAML content to JSON format
76
+ * @param {string} ymlContents - YAML string to convert
77
+ * @returns {string} JSON string representation
78
+ * @throws {Error} If conversion fails
79
+ */
51
80
  function toJson(ymlContents) {
52
81
  let json
53
82
  try {
@@ -61,12 +90,16 @@ function toJson(ymlContents) {
61
90
  // Alias for backward compatibility
62
91
  const matchOutermostBraces = findOutermostBracesDepthFirst
63
92
 
64
-
65
93
  // https://regex101.com/r/XIltbc/1
66
94
  const KEY_OBJECT = /^[ \t]*[^":\s]*:\s+\{/gm
67
95
 
68
96
  const INNER_ARRAY = /\[(?:[^\[\]])*\]/g
69
97
 
98
+ /**
99
+ * Pre-process YAML string to handle nested variables and CloudFormation syntax
100
+ * @param {string} [ymlStr=''] - YAML string to pre-process
101
+ * @returns {string} Pre-processed YAML string
102
+ */
70
103
  function preProcess(ymlStr = '') {
71
104
  /*
72
105
  return ymlStr