config 3.3.11 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/async.js CHANGED
@@ -41,12 +41,11 @@ function resolveAsyncConfigs(config) {
41
41
  var promises = [];
42
42
  var resolvers = [];
43
43
  (function iterate(prop) {
44
- var propsToSort = [];
45
- for (var property in prop) {
46
- if (Object.hasOwnProperty.call(prop, property) && prop[property] != null) {
47
- propsToSort.push(property);
48
- }
44
+ if (prop.constructor === String) {
45
+ return;
49
46
  }
47
+
48
+ var propsToSort = Object.keys(prop).filter((property) => prop[property] != null);
50
49
  propsToSort.sort().forEach(function(property) {
51
50
  if (prop[property].constructor === Object) {
52
51
  iterate(prop[property]);
@@ -54,7 +53,7 @@ function resolveAsyncConfigs(config) {
54
53
  else if (prop[property].constructor === Array) {
55
54
  prop[property].forEach(iterate);
56
55
  }
57
- else if (prop[property] && prop[property].async === asyncSymbol) {
56
+ else if (prop[property].async === asyncSymbol) {
58
57
  resolvers.push(prop[property].prepare(config, prop, property));
59
58
  promises.push(prop[property]);
60
59
  }
package/lib/config.js CHANGED
@@ -7,7 +7,6 @@
7
7
  const DeferredConfig = require('../defer').DeferredConfig;
8
8
  const RawConfig = require('../raw').RawConfig;
9
9
  let Parser = require('../parser');
10
- const Utils = require('util');
11
10
  const Path = require('path');
12
11
  const FileSystem = require('fs');
13
12
 
@@ -415,6 +414,29 @@ util.makeImmutable = function(object, property, value) {
415
414
  // Create a proxy, to capture user updates of configuration options, and throw an exception for awareness, as per:
416
415
  // https://github.com/lorenwest/node-config/issues/514
417
416
  value = new Proxy(util.makeImmutable(value), {
417
+ get(target, property, receiver) {
418
+ // Config's own defined prototype properties and methods (e.g., `get`, `has`, etc.)
419
+ const ownProps = [
420
+ ...Object.getOwnPropertyNames(Config.prototype),
421
+ ...Object.getOwnPropertyNames(target),
422
+ ]
423
+
424
+ // Bypass proxy receiver for properties directly on the target (e.g., RegExp.prototype.source)
425
+ // or properties that are not functions to prevent errors related to internal object methods.
426
+ if (ownProps.includes(property) || (property in target && typeof target[property] !== 'function')) {
427
+ return Reflect.get(target, property);
428
+ }
429
+
430
+ // Otherwise, use the proxy receiver to handle the property access
431
+ const ref = Reflect.get(target, property, receiver);
432
+
433
+ // Binds the method's `this` context to the target object (e.g., Date.prototype.toISOString)
434
+ // to ensure it behaves correctly when called on the proxy.
435
+ if (typeof ref === 'function') {
436
+ return ref.bind(target);
437
+ }
438
+ return ref;
439
+ },
418
440
  set (target, name) {
419
441
  const message = (Reflect.has(target, name) ? 'update' : 'add');
420
442
  // Notify the user.
@@ -494,15 +516,14 @@ util.getOption = function(options, optionName, defaultValue) {
494
516
  * (hostname)-(deployment).EXT
495
517
  * local.EXT
496
518
  * local-(deployment).EXT
497
- * runtime.json
498
519
  * </pre>
499
520
  *
500
521
  * <p>
501
- * EXT can be yml, yaml, coffee, iced, json, cson or js signifying the file type.
522
+ * EXT can be yml, yaml, coffee, iced, json, jsonc, cson or js signifying the file type.
502
523
  * yaml (and yml) is in YAML format, coffee is a coffee-script, iced is iced-coffee-script,
503
- * json is in JSON format, cson is in CSON format, properties is in .properties format
504
- * (http://en.wikipedia.org/wiki/.properties), and js is a javascript executable file that is
505
- * require()'d with module.exports being the config object.
524
+ * json is in JSON format, jsonc is in JSONC format, cson is in CSON format, properties is
525
+ * in .properties format (http://en.wikipedia.org/wiki/.properties), and js is a javascript
526
+ * executable file that is require()'d with module.exports being the config object.
506
527
  * </p>
507
528
  *
508
529
  * <p>
@@ -524,11 +545,6 @@ util.getOption = function(options, optionName, defaultValue) {
524
545
  * </p>
525
546
  *
526
547
  * <p>
527
- * The runtime.json file contains configuration changes made at runtime either
528
- * manually, or by the application setting a configuration value.
529
- * </p>
530
- *
531
- * <p>
532
548
  * If the $NODE_APP_INSTANCE environment variable (or --NODE_APP_INSTANCE
533
549
  * command line parameter) is set, then files with this appendage will be loaded.
534
550
  * See the Multiple Application Instances section of the main documentation page
@@ -577,9 +593,6 @@ util.loadFileConfigs = function(configDir, options) {
577
593
  APP_INSTANCE = util.initParam('NODE_APP_INSTANCE');
578
594
  CONFIG_SKIP_GITCRYPT = util.initParam('CONFIG_SKIP_GITCRYPT');
579
595
 
580
- // This is for backward compatibility
581
- const runtimeFilename = util.initParam('NODE_CONFIG_RUNTIME_JSON', Path.join(dir , 'runtime.json') );
582
-
583
596
  NODE_CONFIG_PARSER = util.initParam('NODE_CONFIG_PARSER');
584
597
  if (NODE_CONFIG_PARSER) {
585
598
  try {
@@ -638,12 +651,15 @@ util.loadFileConfigs = function(configDir, options) {
638
651
  let resolutionIndex = 1;
639
652
  const extNames = Parser.getFilesOrder();
640
653
  baseNames.forEach(function(baseName) {
641
- extNames.forEach(function(extName) {
642
- allowedFiles[baseName + '.' + extName] = resolutionIndex++;
643
- if (APP_INSTANCE) {
644
- allowedFiles[baseName + '-' + APP_INSTANCE + '.' + extName] = resolutionIndex++;
645
- }
646
- });
654
+ const fileNames = [baseName];
655
+ if (APP_INSTANCE) {
656
+ fileNames.push(baseName + '-' + APP_INSTANCE);
657
+ }
658
+ fileNames.forEach(function(fileName) {
659
+ extNames.forEach(function(extName) {
660
+ allowedFiles[fileName + '.' + extName] = resolutionIndex++;
661
+ });
662
+ })
647
663
  });
648
664
 
649
665
  const locatedFiles = util.locateMatchingFiles(dir, allowedFiles);
@@ -703,10 +719,6 @@ util.loadFileConfigs = function(configDir, options) {
703
719
  const customEnvVars = util.getCustomEnvVars(dir, extNames);
704
720
  util.extendDeep(config, customEnvVars);
705
721
 
706
- // Extend the original config with the contents of runtime.json (backwards compatibility)
707
- const runtimeJson = util.parseFile(runtimeFilename) || {};
708
- util.extendDeep(config, runtimeJson);
709
-
710
722
  util.resolveDeferredConfigs(config);
711
723
 
712
724
  // Return the configuration object
@@ -726,19 +738,18 @@ util.loadFileConfigs = function(configDir, options) {
726
738
  */
727
739
  util.locateMatchingFiles = function(configDirs, allowedFiles) {
728
740
  return configDirs.split(Path.delimiter)
741
+ .filter(Boolean)
729
742
  .reduce(function(files, configDir) {
730
- if (configDir) {
731
- configDir = _toAbsolutePath(configDir);
732
- try {
733
- FileSystem.readdirSync(configDir).forEach(function(file) {
734
- if (allowedFiles[file]) {
735
- files.push([allowedFiles[file], Path.join(configDir, file)]);
736
- }
737
- });
738
- }
739
- catch(e) {}
740
- return files;
743
+ configDir = _toAbsolutePath(configDir);
744
+ try {
745
+ FileSystem.readdirSync(configDir)
746
+ .filter(file => allowedFiles[file])
747
+ .forEach(function(file) {
748
+ files.push([allowedFiles[file], Path.join(configDir, file)]);
749
+ });
741
750
  }
751
+ catch(e) {}
752
+ return files;
742
753
  }, [])
743
754
  .sort(function(a, b) { return a[0] - b[0]; })
744
755
  .map(function(file) { return file[1]; });
@@ -749,16 +760,12 @@ util.resolveDeferredConfigs = function (config) {
749
760
  const deferred = [];
750
761
 
751
762
  function _iterate (prop) {
763
+ if (prop.constructor === String) {
764
+ return;
765
+ }
752
766
 
753
767
  // We put the properties we are going to look it in an array to keep the order predictable
754
- const propsToSort = [];
755
-
756
- // First step is to put the properties of interest in an array
757
- for (const property in prop) {
758
- if (Object.hasOwnProperty.call(prop, property) && prop[property] != null) {
759
- propsToSort.push(property);
760
- }
761
- }
768
+ const propsToSort = Object.keys(prop).filter((property) => prop[property] != null);
762
769
 
763
770
  // Second step is to iterate of the elements in a predictable (sorted) order
764
771
  propsToSort.sort().forEach(function (property) {
@@ -798,7 +805,7 @@ util.resolveDeferredConfigs = function (config) {
798
805
  * .js = File to run that has a module.exports containing the config object
799
806
  * .coffee = File to run that has a module.exports with coffee-script containing the config object
800
807
  * .iced = File to run that has a module.exports with iced-coffee-script containing the config object
801
- * All other supported file types (yaml, toml, json, cson, hjson, json5, properties, xml)
808
+ * All other supported file types (yaml, toml, json, jsonc, cson, hjson, json5, properties, xml)
802
809
  * are parsed with util.parseString.
803
810
  *
804
811
  * If the file doesn't exist, a null will be returned. If the file can't be
@@ -873,7 +880,8 @@ util.parseFile = function(fullFilename, options) {
873
880
  *
874
881
  * The format determines the parser to use.
875
882
  *
876
- * json = File is parsed using JSON.parse()
883
+ * json = Parsed with a JSON5 parser
884
+ * jsonc = Parsed with a JSON5 parser
877
885
  * yaml (or yml) = Parsed with a YAML parser
878
886
  * toml = Parsed with a TOML parser
879
887
  * cson = Parsed with a CSON parser
@@ -1003,12 +1011,12 @@ util.cloneDeep = function cloneDeep(parent, depth, circular, prototype) {
1003
1011
  return parent;
1004
1012
  }
1005
1013
 
1006
- if (Utils.isArray(parent)) {
1014
+ if (Array.isArray(parent)) {
1007
1015
  child = [];
1008
- } else if (Utils.isRegExp(parent)) {
1016
+ } else if (parent instanceof RegExp) {
1009
1017
  child = new RegExp(parent.source, util.getRegExpFlags(parent));
1010
1018
  if (parent.lastIndex) child.lastIndex = parent.lastIndex;
1011
- } else if (Utils.isDate(parent)) {
1019
+ } else if (parent instanceof Date) {
1012
1020
  child = new Date(parent.getTime());
1013
1021
  } else if (useBuffer && Buffer.isBuffer(parent)) {
1014
1022
  child = Buffer.alloc(parent.length);
@@ -1278,11 +1286,9 @@ util.diffDeep = function(object1, object2, depth) {
1278
1286
  * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1279
1287
  * @return {object} The altered mergeInto object is returned
1280
1288
  */
1281
- util.extendDeep = function(mergeInto) {
1289
+ util.extendDeep = function(mergeInto, ...vargs) {
1282
1290
 
1283
1291
  // Initialize
1284
- const t = this;
1285
- const vargs = Array.prototype.slice.call(arguments, 1);
1286
1292
  let depth = vargs.pop();
1287
1293
  if (typeof(depth) != 'number') {
1288
1294
  vargs.push(depth);
@@ -1458,8 +1464,11 @@ util.toObject = function(config) {
1458
1464
 
1459
1465
  // Run strictness checks on NODE_ENV and NODE_APP_INSTANCE and throw an error if there's a problem.
1460
1466
  util.runStrictnessChecks = function (config) {
1461
- const sources = config.util.getConfigSources();
1467
+ if (util.initParam('SUPPRESS_STRICTNESS_CHECK')) {
1468
+ return;
1469
+ }
1462
1470
 
1471
+ const sources = config.util.getConfigSources();
1463
1472
  const sourceFilenames = sources.map(function (src) {
1464
1473
  return Path.basename(src.name);
1465
1474
  });
@@ -1514,8 +1523,7 @@ function _toAbsolutePath (configDir) {
1514
1523
  // Instantiate and export the configuration
1515
1524
  const config = module.exports = new Config();
1516
1525
 
1517
- // copy methods to util for backwards compatibility
1518
- util.stripComments = Parser.stripComments;
1526
+ // copy method to util for backwards compatibility
1519
1527
  util.stripYamlComments = Parser.stripYamlComments;
1520
1528
 
1521
1529
  // Produce warnings if the configuration is empty
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "config",
3
- "version": "3.3.11",
3
+ "version": "4.0.0",
4
4
  "main": "./lib/config.js",
5
5
  "description": "Configuration control for production node deployments",
6
6
  "author": "Loren West <open_source@lorenwest.com>",
@@ -30,6 +30,7 @@
30
30
  "cson": "^3.0.1",
31
31
  "hjson": "^1.2.0",
32
32
  "js-yaml": "^3.2.2",
33
+ "nyc": "^15.1.0",
33
34
  "properties": "~1.2.1",
34
35
  "semver": "5.3.0",
35
36
  "toml": "^2.0.6",
@@ -44,9 +45,21 @@
44
45
  "url": "http://github.com/node-config/node-config.git"
45
46
  },
46
47
  "engines": {
47
- "node": ">= 10.0.0"
48
+ "node": ">= 20.0.0"
49
+ },
50
+ "nyc": {
51
+ "include": [
52
+ "*.js",
53
+ "lib/**/*.js"
54
+ ],
55
+ "extension": [
56
+ ".js"
57
+ ],
58
+ "report-dir": "./coverage",
59
+ "reporter": "lcov"
48
60
  },
49
61
  "scripts": {
50
- "test": "./node_modules/vows/bin/vows test/*.js --spec"
62
+ "test": "nyc vows test/*.js --spec",
63
+ "vows": "nyc vows"
51
64
  }
52
65
  }
package/parser.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // External libraries are lazy-loaded only if these file types exist.
2
- const util = require("util");
3
2
 
4
3
  // webpack can't solve dynamic module
5
4
  // @see https://github.com/node-config/node-config/issues/755
@@ -61,7 +60,7 @@ Parser.xmlParser = function(filename, content) {
61
60
  Parser.jsParser = function(filename, content) {
62
61
  var configObject = require(filename);
63
62
 
64
- if (configObject.__esModule && util.isObject(configObject.default)) {
63
+ if (configObject.__esModule && isObject(configObject.default)) {
65
64
  return configObject.default
66
65
  }
67
66
  return configObject;
@@ -209,72 +208,6 @@ Parser.propertiesParser = function(filename, content) {
209
208
  return PPARSER.parse(content, { namespaces: true, variables: true, sections: true });
210
209
  };
211
210
 
212
- /**
213
- * Strip all Javascript type comments from the string.
214
- *
215
- * The string is usually a file loaded from the O/S, containing
216
- * newlines and javascript type comments.
217
- *
218
- * Thanks to James Padolsey, and all who contributed to this implementation.
219
- * http://james.padolsey.com/javascript/javascript-comment-removal-revisted/
220
- *
221
- * @protected
222
- * @method stripComments
223
- * @param fileStr {string} The string to strip comments from
224
- * @param stringRegex {RegExp} Optional regular expression to match strings that
225
- * make up the config file
226
- * @return {string} The string with comments stripped.
227
- */
228
- Parser.stripComments = function(fileStr, stringRegex) {
229
- stringRegex = stringRegex || /"((?:[^"\\]|\\.)*)"/g;
230
-
231
- var uid = '_' + +new Date(),
232
- primitives = [],
233
- primIndex = 0;
234
-
235
- return (
236
- fileStr
237
-
238
- /* Remove strings */
239
- .replace(stringRegex, function(match){
240
- primitives[primIndex] = match;
241
- return (uid + '') + primIndex++;
242
- })
243
-
244
- /* Remove Regexes */
245
- .replace(/([^\/])(\/(?!\*|\/)(\\\/|.)+?\/[gim]{0,3})/g, function(match, $1, $2){
246
- primitives[primIndex] = $2;
247
- return $1 + (uid + '') + primIndex++;
248
- })
249
-
250
- /*
251
- - Remove single-line comments that contain would-be multi-line delimiters
252
- E.g. // Comment /* <--
253
- - Remove multi-line comments that contain would be single-line delimiters
254
- E.g. /* // <--
255
- */
256
- .replace(/\/\/.*?\/?\*.+?(?=\n|\r|$)|\/\*[\s\S]*?\/\/[\s\S]*?\*\//g, '')
257
-
258
- /*
259
- Remove single and multi-line comments,
260
- no consideration of inner-contents
261
- */
262
- .replace(/\/\/.+?(?=\n|\r|$)|\/\*[\s\S]+?\*\//g, '')
263
-
264
- /*
265
- Remove multi-line comments that have a replaced ending (string/regex)
266
- Greedy, so no inner strings/regexes will stop it.
267
- */
268
- .replace(RegExp('\\/\\*[\\s\\S]+' + uid + '\\d+', 'g'), '')
269
-
270
- /* Bring back strings & regexes */
271
- .replace(RegExp(uid + '(\\d+)', 'g'), function(match, n){
272
- return primitives[n];
273
- })
274
- );
275
-
276
- };
277
-
278
211
  /**
279
212
  * Strip YAML comments from the string
280
213
  *
@@ -314,7 +247,7 @@ Parser.numberParser = function(filename, content) {
314
247
  return Number.isNaN(numberValue) ? undefined : numberValue;
315
248
  };
316
249
 
317
- var order = ['js', 'cjs', 'ts', 'json', 'json5', 'hjson', 'toml', 'coffee', 'iced', 'yaml', 'yml', 'cson', 'properties', 'xml',
250
+ var order = ['js', 'cjs', 'mjs', 'ts', 'json', 'jsonc', 'json5', 'hjson', 'toml', 'coffee', 'iced', 'yaml', 'yml', 'cson', 'properties', 'xml',
318
251
  'boolean', 'number'];
319
252
  var definitions = {
320
253
  cjs: Parser.jsParser,
@@ -324,7 +257,9 @@ var definitions = {
324
257
  iced: Parser.icedParser,
325
258
  js: Parser.jsParser,
326
259
  json: Parser.jsonParser,
260
+ jsonc: Parser.jsonParser,
327
261
  json5: Parser.json5Parser,
262
+ mjs: Parser.jsParser,
328
263
  properties: Parser.propertiesParser,
329
264
  toml: Parser.tomlParser,
330
265
  ts: Parser.tsParser,
@@ -366,3 +301,7 @@ Parser.setFilesOrder = function(name, newIndex) {
366
301
  }
367
302
  return order;
368
303
  };
304
+
305
+ function isObject(arg) {
306
+ return (arg !== null) && (typeof arg === 'object');
307
+ }