harthat-chain 2.3.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.

Potentially problematic release.


This version of harthat-chain might be problematic. Click here for more details.

package/lib/config.js ADDED
@@ -0,0 +1,1521 @@
1
+ // config.js (c) 2010-2022 Loren West and other contributors
2
+ // May be freely distributed under the MIT license.
3
+ // For further details and documentation:
4
+ // http://lorenwest.github.com/node-config
5
+
6
+ // Dependencies
7
+ var DeferredConfig = require('../defer').DeferredConfig,
8
+ RawConfig = require('../raw').RawConfig,
9
+ Parser = require('../parser'),
10
+ Utils = require('util'),
11
+ Path = require('path'),
12
+ FileSystem = require('fs');
13
+
14
+ // Static members
15
+ var DEFAULT_CLONE_DEPTH = 20,
16
+ CONFIG_DIR, NODE_ENV, APP_INSTANCE,
17
+ CONFIG_SKIP_GITCRYPT, NODE_ENV_VAR_NAME,
18
+ NODE_CONFIG_PARSER,
19
+ env = {},
20
+ configSources = [], // Configuration sources - array of {name, original, parsed}
21
+ checkMutability = true, // Check for mutability/immutability on first get
22
+ gitCryptTestRegex = /^.GITCRYPT/; // regular expression to test for gitcrypt files.
23
+
24
+ /**
25
+ * <p>Application Configurations</p>
26
+ *
27
+ * <p>
28
+ * The config module exports a singleton object representing all
29
+ * configurations for this application deployment.
30
+ * </p>
31
+ *
32
+ * <p>
33
+ * Application configurations are stored in files within the config directory
34
+ * of your application. The default configuration file is loaded, followed
35
+ * by files specific to the deployment type (development, testing, staging,
36
+ * production, etc.).
37
+ * </p>
38
+ *
39
+ * <p>
40
+ * For example, with the following config/default.yaml file:
41
+ * </p>
42
+ *
43
+ * <pre>
44
+ * ...
45
+ * customer:
46
+ * &nbsp;&nbsp;initialCredit: 500
47
+ * &nbsp;&nbsp;db:
48
+ * &nbsp;&nbsp;&nbsp;&nbsp;name: customer
49
+ * &nbsp;&nbsp;&nbsp;&nbsp;port: 5984
50
+ * ...
51
+ * </pre>
52
+ *
53
+ * <p>
54
+ * The following code loads the customer section into the CONFIG variable:
55
+ * <p>
56
+ *
57
+ * <pre>
58
+ * var CONFIG = require('config').customer;
59
+ * ...
60
+ * newCustomer.creditLimit = CONFIG.initialCredit;
61
+ * database.open(CONFIG.db.name, CONFIG.db.port);
62
+ * ...
63
+ * </pre>
64
+ *
65
+ * @module config
66
+ * @class Config
67
+ */
68
+
69
+ /**
70
+ * <p>Get the configuration object.</p>
71
+ *
72
+ * <p>
73
+ * The configuration object is a shared singleton object within the application,
74
+ * attained by calling require('config').
75
+ * </p>
76
+ *
77
+ * <p>
78
+ * Usually you'll specify a CONFIG variable at the top of your .js file
79
+ * for file/module scope. If you want the root of the object, you can do this:
80
+ * </p>
81
+ * <pre>
82
+ * var CONFIG = require('config');
83
+ * </pre>
84
+ *
85
+ * <p>
86
+ * Sometimes you only care about a specific sub-object within the CONFIG
87
+ * object. In that case you could do this at the top of your file:
88
+ * </p>
89
+ * <pre>
90
+ * var CONFIG = require('config').customer;
91
+ * or
92
+ * var CUSTOMER_CONFIG = require('config').customer;
93
+ * </pre>
94
+ *
95
+ * <script type="text/javascript">
96
+ * document.getElementById("showProtected").style.display = "block";
97
+ * </script>
98
+ *
99
+ * @method constructor
100
+ * @return CONFIG {object} - The top level configuration object
101
+ */
102
+ var Config = function() {
103
+ var t = this;
104
+
105
+ // Bind all utility functions to this
106
+ for (var fnName in util) {
107
+ if (typeof util[fnName] === 'function') {
108
+ util[fnName] = util[fnName].bind(t);
109
+ }
110
+ }
111
+
112
+ // Merge configurations into this
113
+ util.extendDeep(t, util.loadFileConfigs());
114
+ util.attachProtoDeep(t);
115
+
116
+ // Perform strictness checks and possibly throw an exception.
117
+ util.runStrictnessChecks(t);
118
+ };
119
+
120
+ /**
121
+ * Utilities are under the util namespace vs. at the top level
122
+ */
123
+ var util = Config.prototype.util = {};
124
+
125
+ /**
126
+ * Underlying get mechanism
127
+ *
128
+ * @private
129
+ * @method getImpl
130
+ * @param object {object} - Object to get the property for
131
+ * @param property {string|string[]} - The property name to get (as an array or '.' delimited string)
132
+ * @return value {*} - Property value, including undefined if not defined.
133
+ */
134
+ var getImpl= function(object, property) {
135
+ var t = this,
136
+ elems = Array.isArray(property) ? property : property.split('.'),
137
+ name = elems[0],
138
+ value = object[name];
139
+ if (elems.length <= 1) {
140
+ return value;
141
+ }
142
+ // Note that typeof null === 'object'
143
+ if (value === null || typeof value !== 'object') {
144
+ return undefined;
145
+ }
146
+ return getImpl(value, elems.slice(1));
147
+ };
148
+
149
+ /**
150
+ * <p>Get a configuration value</p>
151
+ *
152
+ * <p>
153
+ * This will return the specified property value, throwing an exception if the
154
+ * configuration isn't defined. It is used to assure configurations are defined
155
+ * before being used, and to prevent typos.
156
+ * </p>
157
+ *
158
+ * @method get
159
+ * @param property {string} - The configuration property to get. Can include '.' sub-properties.
160
+ * @return value {*} - The property value
161
+ */
162
+ Config.prototype.get = function(property) {
163
+ if(property === null || property === undefined){
164
+ throw new Error("Calling config.get with null or undefined argument");
165
+ }
166
+
167
+ // Make configurations immutable after first get (unless disabled)
168
+ if (checkMutability) {
169
+ if (!util.initParam('ALLOW_CONFIG_MUTATIONS', false)) {
170
+ util.makeImmutable(config);
171
+ }
172
+ checkMutability = false;
173
+ }
174
+ var t = this,
175
+ value = getImpl(t, property);
176
+
177
+ // Produce an exception if the property doesn't exist
178
+ if (value === undefined) {
179
+ throw new Error('Configuration property "' + property + '" is not defined');
180
+ }
181
+
182
+ // Return the value
183
+ return value;
184
+ };
185
+
186
+ /**
187
+ * Test that a configuration parameter exists
188
+ *
189
+ * <pre>
190
+ * var config = require('config');
191
+ * if (config.has('customer.dbName')) {
192
+ * console.log('Customer database name: ' + config.customer.dbName);
193
+ * }
194
+ * </pre>
195
+ *
196
+ * @method has
197
+ * @param property {string} - The configuration property to test. Can include '.' sub-properties.
198
+ * @return isPresent {boolean} - True if the property is defined, false if not defined.
199
+ */
200
+ Config.prototype.has = function(property) {
201
+ // While get() throws an exception for undefined input, has() is designed to test validity, so false is appropriate
202
+ if(property === null || property === undefined){
203
+ return false;
204
+ }
205
+ var t = this;
206
+ return (getImpl(t, property) !== undefined);
207
+ };
208
+
209
+ /**
210
+ * <p>
211
+ * Set default configurations for a node.js module.
212
+ * </p>
213
+ *
214
+ * <p>
215
+ * This allows module developers to attach their configurations onto the
216
+ * default configuration object so they can be configured by the consumers
217
+ * of the module.
218
+ * </p>
219
+ *
220
+ * <p>Using the function within your module:</p>
221
+ * <pre>
222
+ * var CONFIG = require("config");
223
+ * CONFIG.util.setModuleDefaults("MyModule", {
224
+ * &nbsp;&nbsp;templateName: "t-50",
225
+ * &nbsp;&nbsp;colorScheme: "green"
226
+ * });
227
+ * <br>
228
+ * // Template name may be overridden by application config files
229
+ * console.log("Template: " + CONFIG.MyModule.templateName);
230
+ * </pre>
231
+ *
232
+ * <p>
233
+ * The above example results in a "MyModule" element of the configuration
234
+ * object, containing an object with the specified default values.
235
+ * </p>
236
+ *
237
+ * @method setModuleDefaults
238
+ * @param moduleName {string} - Name of your module.
239
+ * @param defaultProperties {object} - The default module configuration.
240
+ * @return moduleConfig {object} - The module level configuration object.
241
+ */
242
+ util.setModuleDefaults = function (moduleName, defaultProperties) {
243
+
244
+ // Copy the properties into a new object
245
+ var t = this,
246
+ moduleConfig = util.cloneDeep(defaultProperties);
247
+
248
+ // Set module defaults into the first sources element
249
+ if (configSources.length === 0 || configSources[0].name !== 'Module Defaults') {
250
+ configSources.splice(0, 0, {
251
+ name: 'Module Defaults',
252
+ parsed: {}
253
+ });
254
+ }
255
+ util.setPath(configSources[0].parsed, moduleName.split('.'), {});
256
+ util.extendDeep(getImpl(configSources[0].parsed, moduleName), defaultProperties);
257
+
258
+ // Create a top level config for this module if it doesn't exist
259
+ util.setPath(t, moduleName.split('.'), getImpl(t, moduleName) || {});
260
+
261
+ // Extend local configurations into the module config
262
+ util.extendDeep(moduleConfig, getImpl(t, moduleName));
263
+
264
+ // Merge the extended configs without replacing the original
265
+ util.extendDeep(getImpl(t, moduleName), moduleConfig);
266
+
267
+ // reset the mutability check for "config.get" method.
268
+ // we are not making t[moduleName] immutable immediately,
269
+ // since there might be more modifications before the first config.get
270
+ if (!util.initParam('ALLOW_CONFIG_MUTATIONS', false)) {
271
+ checkMutability = true;
272
+ }
273
+
274
+ // Attach handlers & watchers onto the module config object
275
+ return util.attachProtoDeep(getImpl(t, moduleName));
276
+ };
277
+
278
+ /**
279
+ * <p>Make a configuration property hidden so it doesn't appear when enumerating
280
+ * elements of the object.</p>
281
+ *
282
+ * <p>
283
+ * The property still exists and can be read from and written to, but it won't
284
+ * show up in for ... in loops, Object.keys(), or JSON.stringify() type methods.
285
+ * </p>
286
+ *
287
+ * <p>
288
+ * If the property already exists, it will be made hidden. Otherwise it will
289
+ * be created as a hidden property with the specified value.
290
+ * </p>
291
+ *
292
+ * <p><i>
293
+ * This method was built for hiding configuration values, but it can be applied
294
+ * to <u>any</u> javascript object.
295
+ * </i></p>
296
+ *
297
+ * <p>Example:</p>
298
+ * <pre>
299
+ * var CONFIG = require('config');
300
+ * ...
301
+ *
302
+ * // Hide the Amazon S3 credentials
303
+ * CONFIG.util.makeHidden(CONFIG.amazonS3, 'access_id');
304
+ * CONFIG.util.makeHidden(CONFIG.amazonS3, 'secret_key');
305
+ * </pre>
306
+ *
307
+ * @method makeHidden
308
+ * @param object {object} - The object to make a hidden property into.
309
+ * @param property {string} - The name of the property to make hidden.
310
+ * @param value {*} - (optional) Set the property value to this (otherwise leave alone)
311
+ * @return object {object} - The original object is returned - for chaining.
312
+ */
313
+ util.makeHidden = function(object, property, value) {
314
+
315
+ // If the new value isn't specified, just mark the property as hidden
316
+ if (typeof value === 'undefined') {
317
+ Object.defineProperty(object, property, {
318
+ enumerable : false
319
+ });
320
+ }
321
+ // Otherwise set the value and mark it as hidden
322
+ else {
323
+ Object.defineProperty(object, property, {
324
+ value : value,
325
+ enumerable : false
326
+ });
327
+ }
328
+
329
+ return object;
330
+ }
331
+
332
+ /**
333
+ * <p>Make a javascript object property immutable (assuring it cannot be changed
334
+ * from the current value).</p>
335
+ * <p>
336
+ * If the specified property is an object, all attributes of that object are
337
+ * made immutable, including properties of contained objects, recursively.
338
+ * If a property name isn't supplied, all properties of the object are made
339
+ * immutable.
340
+ * </p>
341
+ * <p>
342
+ *
343
+ * </p>
344
+ * <p>
345
+ * New properties can be added to the object and those properties will not be
346
+ * immutable unless this method is called on those new properties.
347
+ * </p>
348
+ * <p>
349
+ * This operation cannot be undone.
350
+ * </p>
351
+ *
352
+ * <p>Example:</p>
353
+ * <pre>
354
+ * var config = require('config');
355
+ * var myObject = {hello:'world'};
356
+ * config.util.makeImmutable(myObject);
357
+ * </pre>
358
+ *
359
+ * @method makeImmutable
360
+ * @param object {object} - The object to specify immutable properties for
361
+ * @param [property] {string | [string]} - The name of the property (or array of names) to make immutable.
362
+ * If not provided, all owned properties of the object are made immutable.
363
+ * @param [value] {* | [*]} - Property value (or array of values) to set
364
+ * the property to before making immutable. Only used when setting a single
365
+ * property. Retained for backward compatibility.
366
+ * @return object {object} - The original object is returned - for chaining.
367
+ */
368
+ util.makeImmutable = function(object, property, value) {
369
+ if (Buffer.isBuffer(object)) {
370
+ return object;
371
+ }
372
+ var properties = null;
373
+
374
+ // Backwards compatibility mode where property/value can be specified
375
+ if (typeof property === 'string') {
376
+ return Object.defineProperty(object, property, {
377
+ value : (typeof value === 'undefined') ? object[property] : value,
378
+ writable : false,
379
+ configurable: false
380
+ });
381
+ }
382
+
383
+ // Get the list of properties to work with
384
+ if (Array.isArray(property)) {
385
+ properties = property;
386
+ }
387
+ else {
388
+ properties = Object.keys(object);
389
+ }
390
+
391
+ // Process each property
392
+ for (var i = 0; i < properties.length; i++) {
393
+ var propertyName = properties[i],
394
+ value = object[propertyName];
395
+
396
+ if (value instanceof RawConfig) {
397
+ Object.defineProperty(object, propertyName, {
398
+ value: value.resolve(),
399
+ writable: false,
400
+ configurable: false
401
+ });
402
+ } else if (Array.isArray(value)) {
403
+ // Ensure object items of this array are also immutable.
404
+ value.forEach((item, index) => { if (util.isObject(item) || Array.isArray(item)) util.makeImmutable(item) })
405
+
406
+ Object.defineProperty(object, propertyName, {
407
+ value: Object.freeze(value)
408
+ });
409
+ } else {
410
+ // Call recursively if an object.
411
+ if (util.isObject(value)) {
412
+ // Create a proxy, to capture user updates of configuration options, and throw an exception for awareness, as per:
413
+ // https://github.com/lorenwest/node-config/issues/514
414
+ value = new Proxy(util.makeImmutable(value), {
415
+ set (target, name) {
416
+ const message = (Reflect.has(target, name) ? 'update' : 'add');
417
+ // Notify the user.
418
+ throw Error(`Can not ${message} runtime configuration property: "${name}". Configuration objects are immutable unless ALLOW_CONFIG_MUTATIONS is set.`)
419
+ }
420
+ })
421
+ }
422
+
423
+ Object.defineProperty(object, propertyName, {
424
+ value: value,
425
+ writable : false,
426
+ configurable: false
427
+ });
428
+
429
+ // Ensure new properties can not be added, as per:
430
+ // https://github.com/lorenwest/node-config/issues/505
431
+ Object.preventExtensions(object[propertyName])
432
+ }
433
+ }
434
+
435
+ return object;
436
+ };
437
+
438
+ /**
439
+ * Return the sources for the configurations
440
+ *
441
+ * <p>
442
+ * All sources for configurations are stored in an array of objects containing
443
+ * the source name (usually the filename), the original source (as a string),
444
+ * and the parsed source as an object.
445
+ * </p>
446
+ *
447
+ * @method getConfigSources
448
+ * @return configSources {Array[Object]} - An array of objects containing
449
+ * name, original, and parsed elements
450
+ */
451
+ util.getConfigSources = function() {
452
+ var t = this;
453
+ return configSources.slice(0);
454
+ };
455
+
456
+ /**
457
+ * Looks into an options object for a specific attribute
458
+ *
459
+ * <p>
460
+ * This method looks into the options object, and if an attribute is defined, returns it,
461
+ * and if not, returns the default value
462
+ * </p>
463
+ *
464
+ * @method getOption
465
+ * @param options {Object | undefined} the options object
466
+ * @param optionName {string} the attribute name to look for
467
+ * @param defaultValue { any } the default in case the options object is empty, or the attribute does not exist.
468
+ * @return options[optionName] if defined, defaultValue if not.
469
+ */
470
+ util.getOption = function(options, optionName, defaultValue) {
471
+ if (options !== undefined && options[optionName] !== undefined){
472
+ return options[optionName];
473
+ } else {
474
+ return defaultValue;
475
+ }
476
+ };
477
+
478
+
479
+ /**
480
+ * Load the individual file configurations.
481
+ *
482
+ * <p>
483
+ * This method builds a map of filename to the configuration object defined
484
+ * by the file. The search order is:
485
+ * </p>
486
+ *
487
+ * <pre>
488
+ * default.EXT
489
+ * (deployment).EXT
490
+ * (hostname).EXT
491
+ * (hostname)-(deployment).EXT
492
+ * local.EXT
493
+ * local-(deployment).EXT
494
+ * runtime.json
495
+ * </pre>
496
+ *
497
+ * <p>
498
+ * EXT can be yml, yaml, coffee, iced, json, cson or js signifying the file type.
499
+ * yaml (and yml) is in YAML format, coffee is a coffee-script, iced is iced-coffee-script,
500
+ * json is in JSON format, cson is in CSON format, properties is in .properties format
501
+ * (http://en.wikipedia.org/wiki/.properties), and js is a javascript executable file that is
502
+ * require()'d with module.exports being the config object.
503
+ * </p>
504
+ *
505
+ * <p>
506
+ * hostname is the $HOST environment variable (or --HOST command line parameter)
507
+ * if set, otherwise the $HOSTNAME environment variable (or --HOSTNAME command
508
+ * line parameter) if set, otherwise the hostname found from
509
+ * require('os').hostname().
510
+ * </p>
511
+ *
512
+ * <p>
513
+ * Once a hostname is found, everything from the first period ('.') onwards
514
+ * is removed. For example, abc.example.com becomes abc
515
+ * </p>
516
+ *
517
+ * <p>
518
+ * (deployment) is the deployment type, found in the $NODE_ENV environment
519
+ * variable (which can be overridden by using $NODE_CONFIG_ENV
520
+ * environment variable). Defaults to 'development'.
521
+ * </p>
522
+ *
523
+ * <p>
524
+ * The runtime.json file contains configuration changes made at runtime either
525
+ * manually, or by the application setting a configuration value.
526
+ * </p>
527
+ *
528
+ * <p>
529
+ * If the $NODE_APP_INSTANCE environment variable (or --NODE_APP_INSTANCE
530
+ * command line parameter) is set, then files with this appendage will be loaded.
531
+ * See the Multiple Application Instances section of the main documentation page
532
+ * for more information.
533
+ * </p>
534
+ *
535
+ * @protected
536
+ * @method loadFileConfigs
537
+ * @param configDir { string | null } the path to the directory containing the configurations to load
538
+ * @param options { object | undefined } parsing options. Current supported option: skipConfigSources: true|false
539
+ * @return config {Object} The configuration object
540
+ */
541
+ util.loadFileConfigs = function(configDir, options) {
542
+
543
+ // Initialize
544
+ var t = this,
545
+ config = {};
546
+
547
+ // Specify variables that can be used to define the environment
548
+ var node_env_var_names = ['NODE_CONFIG_ENV', 'NODE_ENV'];
549
+
550
+ // Loop through the variables to try and set environment
551
+ for (const node_env_var_name of node_env_var_names) {
552
+ NODE_ENV = util.initParam(node_env_var_name);
553
+ if (!!NODE_ENV) {
554
+ NODE_ENV_VAR_NAME = node_env_var_name;
555
+ break;
556
+ }
557
+ }
558
+
559
+ // If we haven't successfully set the environment using the variables, we'll default it
560
+ if (!NODE_ENV) {
561
+ NODE_ENV = 'development';
562
+ }
563
+
564
+ node_env_var_names.forEach(node_env_var_name => {
565
+ env[node_env_var_name] = NODE_ENV;
566
+ });
567
+
568
+ // Split files name, for loading multiple files.
569
+ NODE_ENV = NODE_ENV.split(',');
570
+
571
+ var dir = configDir || util.initParam('NODE_CONFIG_DIR', Path.join( process.cwd(), 'config') );
572
+ dir = _toAbsolutePath(dir);
573
+
574
+ APP_INSTANCE = util.initParam('NODE_APP_INSTANCE');
575
+ CONFIG_SKIP_GITCRYPT = util.initParam('CONFIG_SKIP_GITCRYPT');
576
+
577
+ // This is for backward compatibility
578
+ var runtimeFilename = util.initParam('NODE_CONFIG_RUNTIME_JSON', Path.join(dir , 'runtime.json') );
579
+
580
+ NODE_CONFIG_PARSER = util.initParam('NODE_CONFIG_PARSER');
581
+ if (NODE_CONFIG_PARSER) {
582
+ try {
583
+ var parserModule = Path.isAbsolute(NODE_CONFIG_PARSER)
584
+ ? NODE_CONFIG_PARSER
585
+ : Path.join(dir, NODE_CONFIG_PARSER);
586
+ Parser = require(parserModule);
587
+ }
588
+ catch (e) {
589
+ console.warn('Failed to load config parser from ' + NODE_CONFIG_PARSER);
590
+ console.log(e);
591
+ }
592
+ }
593
+
594
+ var HOST = util.initParam('HOST');
595
+ var HOSTNAME = util.initParam('HOSTNAME');
596
+
597
+ // Determine the host name from the OS module, $HOST, or $HOSTNAME
598
+ // Remove any . appendages, and default to null if not set
599
+ try {
600
+ var hostName = HOST || HOSTNAME;
601
+
602
+ if (!hostName) {
603
+ var OS = require('os');
604
+ hostName = OS.hostname();
605
+ }
606
+ } catch (e) {
607
+ hostName = '';
608
+ }
609
+
610
+ // Store the hostname that won.
611
+ env.HOSTNAME = hostName;
612
+
613
+ // Read each file in turn
614
+ var baseNames = ['default'].concat(NODE_ENV);
615
+
616
+ // #236: Also add full hostname when they are different.
617
+ if (hostName) {
618
+ var firstDomain = hostName.split('.')[0];
619
+
620
+ NODE_ENV.forEach(function(env) {
621
+ // Backward compatibility
622
+ baseNames.push(firstDomain, firstDomain + '-' + env);
623
+
624
+ // Add full hostname when it is not the same
625
+ if (hostName !== firstDomain) {
626
+ baseNames.push(hostName, hostName + '-' + env);
627
+ }
628
+ });
629
+ }
630
+
631
+ NODE_ENV.forEach(function(env) {
632
+ baseNames.push('local', 'local-' + env);
633
+ });
634
+
635
+ var allowedFiles = {};
636
+ var resolutionIndex = 1;
637
+ var extNames = Parser.getFilesOrder();
638
+ baseNames.forEach(function(baseName) {
639
+ extNames.forEach(function(extName) {
640
+ allowedFiles[baseName + '.' + extName] = resolutionIndex++;
641
+ if (APP_INSTANCE) {
642
+ allowedFiles[baseName + '-' + APP_INSTANCE + '.' + extName] = resolutionIndex++;
643
+ }
644
+ });
645
+ });
646
+
647
+ var locatedFiles = util.locateMatchingFiles(dir, allowedFiles);
648
+ locatedFiles.forEach(function(fullFilename) {
649
+ var configObj = util.parseFile(fullFilename, options);
650
+ if (configObj) {
651
+ util.extendDeep(config, configObj);
652
+ }
653
+ });
654
+
655
+ // Override configurations from the $NODE_CONFIG environment variable
656
+ // NODE_CONFIG only applies to the base config
657
+ if (!configDir) {
658
+ var envConfig = {};
659
+
660
+ CONFIG_DIR = dir;
661
+
662
+ if (process.env.NODE_CONFIG) {
663
+ try {
664
+ envConfig = JSON.parse(process.env.NODE_CONFIG);
665
+ } catch(e) {
666
+ console.error('The $NODE_CONFIG environment variable is malformed JSON');
667
+ }
668
+ util.extendDeep(config, envConfig);
669
+ var skipConfigSources = util.getOption(options,'skipConfigSources', false);
670
+ if (!skipConfigSources){
671
+ configSources.push({
672
+ name: "$NODE_CONFIG",
673
+ parsed: envConfig,
674
+ });
675
+ }
676
+ }
677
+
678
+ // Override configurations from the --NODE_CONFIG command line
679
+ var cmdLineConfig = util.getCmdLineArg('NODE_CONFIG');
680
+ if (cmdLineConfig) {
681
+ try {
682
+ cmdLineConfig = JSON.parse(cmdLineConfig);
683
+ } catch(e) {
684
+ console.error('The --NODE_CONFIG={json} command line argument is malformed JSON');
685
+ }
686
+ util.extendDeep(config, cmdLineConfig);
687
+ var skipConfigSources = util.getOption(options,'skipConfigSources', false);
688
+ if (!skipConfigSources){
689
+ configSources.push({
690
+ name: "--NODE_CONFIG argument",
691
+ parsed: cmdLineConfig,
692
+ });
693
+ }
694
+ }
695
+
696
+ // Place the mixed NODE_CONFIG into the environment
697
+ env['NODE_CONFIG'] = JSON.stringify(util.extendDeep(envConfig, cmdLineConfig, {}));
698
+ }
699
+
700
+ // Override with environment variables if there is a custom-environment-variables.EXT mapping file
701
+ var customEnvVars = util.getCustomEnvVars(dir, extNames);
702
+ util.extendDeep(config, customEnvVars);
703
+
704
+ // Extend the original config with the contents of runtime.json (backwards compatibility)
705
+ var runtimeJson = util.parseFile(runtimeFilename) || {};
706
+ util.extendDeep(config, runtimeJson);
707
+
708
+ util.resolveDeferredConfigs(config);
709
+
710
+ // Return the configuration object
711
+ return config;
712
+ };
713
+
714
+ /**
715
+ * Return a list of fullFilenames who exists in allowedFiles
716
+ * Ordered according to allowedFiles argument specifications
717
+ *
718
+ * @protected
719
+ * @method locateMatchingFiles
720
+ * @param configDirs {string} the config dir, or multiple dirs separated by a column (:)
721
+ * @param allowedFiles {object} an object. keys and supported filenames
722
+ * and values are the position in the resolution order
723
+ * @returns {string[]} fullFilenames - path + filename
724
+ */
725
+ util.locateMatchingFiles = function(configDirs, allowedFiles) {
726
+ return configDirs.split(Path.delimiter)
727
+ .reduce(function(files, configDir) {
728
+ if (configDir) {
729
+ configDir = _toAbsolutePath(configDir);
730
+ try {
731
+ FileSystem.readdirSync(configDir).forEach(function(file) {
732
+ if (allowedFiles[file]) {
733
+ files.push([allowedFiles[file], Path.join(configDir, file)]);
734
+ }
735
+ });
736
+ }
737
+ catch(e) {}
738
+ return files;
739
+ }
740
+ }, [])
741
+ .sort(function(a, b) { return a[0] - b[0]; })
742
+ .map(function(file) { return file[1]; });
743
+ };
744
+
745
+ // Using basic recursion pattern, find all the deferred values and resolve them.
746
+ util.resolveDeferredConfigs = function (config) {
747
+ var deferred = [];
748
+
749
+ function _iterate (prop) {
750
+
751
+ // We put the properties we are going to look it in an array to keep the order predictable
752
+ var propsToSort = [];
753
+
754
+ // First step is to put the properties of interest in an array
755
+ for (var property in prop) {
756
+ if (Object.hasOwnProperty.call(prop, property) && prop[property] != null) {
757
+ propsToSort.push(property);
758
+ }
759
+ }
760
+
761
+ // Second step is to iterate of the elements in a predictable (sorted) order
762
+ propsToSort.sort().forEach(function (property) {
763
+ if (prop[property].constructor === Object) {
764
+ _iterate(prop[property]);
765
+ } else if (prop[property].constructor === Array) {
766
+ for (var i = 0; i < prop[property].length; i++) {
767
+ if (prop[property][i] instanceof DeferredConfig) {
768
+ deferred.push(prop[property][i].prepare(config, prop[property], i));
769
+ }
770
+ else {
771
+ _iterate(prop[property][i]);
772
+ }
773
+ }
774
+ } else {
775
+ if (prop[property] instanceof DeferredConfig) {
776
+ deferred.push(prop[property].prepare(config, prop, property));
777
+ }
778
+ // else: Nothing to do. Keep the property how it is.
779
+ }
780
+ });
781
+ }
782
+
783
+ _iterate(config);
784
+
785
+ deferred.forEach(function (defer) { defer.resolve(); });
786
+ };
787
+
788
+ /**
789
+ * Parse and return the specified configuration file.
790
+ *
791
+ * If the file exists in the application config directory, it will
792
+ * parse and return it as a JavaScript object.
793
+ *
794
+ * The file extension determines the parser to use.
795
+ *
796
+ * .js = File to run that has a module.exports containing the config object
797
+ * .coffee = File to run that has a module.exports with coffee-script containing the config object
798
+ * .iced = File to run that has a module.exports with iced-coffee-script containing the config object
799
+ * All other supported file types (yaml, toml, json, cson, hjson, json5, properties, xml)
800
+ * are parsed with util.parseString.
801
+ *
802
+ * If the file doesn't exist, a null will be returned. If the file can't be
803
+ * parsed, an exception will be thrown.
804
+ *
805
+ * This method performs synchronous file operations, and should not be called
806
+ * after synchronous module loading.
807
+ *
808
+ * @protected
809
+ * @method parseFile
810
+ * @param fullFilename {string} The full file path and name
811
+ * @param options { object | undefined } parsing options. Current supported option: skipConfigSources: true|false
812
+ * @return configObject {object|null} The configuration object parsed from the file
813
+ */
814
+ util.parseFile = function(fullFilename, options) {
815
+ var t = this, // Initialize
816
+ configObject = null,
817
+ fileContent = null,
818
+ stat = null;
819
+
820
+ // Note that all methods here are the Sync versions. This is appropriate during
821
+ // module loading (which is a synchronous operation), but not thereafter.
822
+
823
+ try {
824
+ // Try loading the file.
825
+ fileContent = FileSystem.readFileSync(fullFilename, 'utf-8');
826
+ fileContent = fileContent.replace(/^\uFEFF/, '');
827
+ }
828
+ catch (e2) {
829
+ if (e2.code !== 'ENOENT') {
830
+ throw new Error('Config file ' + fullFilename + ' cannot be read. Error code is: '+e2.code
831
+ +'. Error message is: '+e2.message);
832
+ }
833
+ return null; // file doesn't exists
834
+ }
835
+
836
+ // Parse the file based on extension
837
+ try {
838
+
839
+ // skip if it's a gitcrypt file and CONFIG_SKIP_GITCRYPT is true
840
+ if (CONFIG_SKIP_GITCRYPT) {
841
+ if (gitCryptTestRegex.test(fileContent)) {
842
+ console.error('WARNING: ' + fullFilename + ' is a git-crypt file and CONFIG_SKIP_GITCRYPT is set. skipping.');
843
+ return null;
844
+ }
845
+ }
846
+
847
+ configObject = Parser.parse(fullFilename, fileContent);
848
+ }
849
+ catch (e3) {
850
+ if (gitCryptTestRegex.test(fileContent)) {
851
+ console.error('ERROR: ' + fullFilename + ' is a git-crypt file and CONFIG_SKIP_GITCRYPT is not set.');
852
+ }
853
+ throw new Error("Cannot parse config file: '" + fullFilename + "': " + e3);
854
+ }
855
+
856
+ // Keep track of this configuration sources, including empty ones, unless the skipConfigSources flag is set to true in the options
857
+ var skipConfigSources = util.getOption(options,'skipConfigSources', false);
858
+ if (typeof configObject === 'object' && !skipConfigSources) {
859
+ configSources.push({
860
+ name: fullFilename,
861
+ original: fileContent,
862
+ parsed: configObject,
863
+ });
864
+ }
865
+
866
+ return configObject;
867
+ };
868
+
869
+ /**
870
+ * Parse and return the specified string with the specified format.
871
+ *
872
+ * The format determines the parser to use.
873
+ *
874
+ * json = File is parsed using JSON.parse()
875
+ * yaml (or yml) = Parsed with a YAML parser
876
+ * toml = Parsed with a TOML parser
877
+ * cson = Parsed with a CSON parser
878
+ * hjson = Parsed with a HJSON parser
879
+ * json5 = Parsed with a JSON5 parser
880
+ * properties = Parsed with the 'properties' node package
881
+ * xml = Parsed with a XML parser
882
+ *
883
+ * If the file doesn't exist, a null will be returned. If the file can't be
884
+ * parsed, an exception will be thrown.
885
+ *
886
+ * This method performs synchronous file operations, and should not be called
887
+ * after synchronous module loading.
888
+ *
889
+ * @protected
890
+ * @method parseString
891
+ * @param content {string} The full content
892
+ * @param format {string} The format to be parsed
893
+ * @return {configObject} The configuration object parsed from the string
894
+ */
895
+ util.parseString = function (content, format) {
896
+ var parser = Parser.getParser(format);
897
+ if (typeof parser === 'function') {
898
+ return parser(null, content);
899
+ }
900
+ };
901
+
902
+ /**
903
+ * Attach the Config class prototype to all config objects recursively.
904
+ *
905
+ * <p>
906
+ * This allows you to do anything with CONFIG sub-objects as you can do with
907
+ * the top-level CONFIG object. It's so you can do this:
908
+ * </p>
909
+ *
910
+ * <pre>
911
+ * var CUST_CONFIG = require('config').Customer;
912
+ * CUST_CONFIG.get(...)
913
+ * </pre>
914
+ *
915
+ * @protected
916
+ * @method attachProtoDeep
917
+ * @param toObject
918
+ * @param depth
919
+ * @return toObject
920
+ */
921
+ util.attachProtoDeep = function(toObject, depth) {
922
+ if (toObject instanceof RawConfig) {
923
+ return toObject;
924
+ }
925
+
926
+ // Recursion detection
927
+ var t = this;
928
+ depth = (depth === null ? DEFAULT_CLONE_DEPTH : depth);
929
+ if (depth < 0) {
930
+ return toObject;
931
+ }
932
+
933
+ // Adding Config.prototype methods directly to toObject as hidden properties
934
+ // because adding to toObject.__proto__ exposes the function in toObject
935
+ for (var fnName in Config.prototype) {
936
+ if (!toObject[fnName]) {
937
+ util.makeHidden(toObject, fnName, Config.prototype[fnName]);
938
+ }
939
+ }
940
+
941
+ // Add prototypes to sub-objects
942
+ for (var prop in toObject) {
943
+ if (util.isObject(toObject[prop])) {
944
+ util.attachProtoDeep(toObject[prop], depth - 1);
945
+ }
946
+ }
947
+
948
+ // Return the original object
949
+ return toObject;
950
+ };
951
+
952
+ /**
953
+ * Return a deep copy of the specified object.
954
+ *
955
+ * This returns a new object with all elements copied from the specified
956
+ * object. Deep copies are made of objects and arrays so you can do anything
957
+ * with the returned object without affecting the input object.
958
+ *
959
+ * @protected
960
+ * @method cloneDeep
961
+ * @param parent {object} The original object to copy from
962
+ * @param [depth=20] {Integer} Maximum depth (default 20)
963
+ * @return {object} A new object with the elements copied from the copyFrom object
964
+ *
965
+ * This method is copied from https://github.com/pvorb/node-clone/blob/17eea36140d61d97a9954c53417d0e04a00525d9/clone.js
966
+ *
967
+ * Copyright © 2011-2014 Paul Vorbach and contributors.
968
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
969
+ * of this software and associated documentation files (the “Software”), to deal
970
+ * in the Software without restriction, including without limitation the rights
971
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
972
+ * of the Software, and to permit persons to whom the Software is furnished to do so,
973
+ * subject to the following conditions: The above copyright notice and this permission
974
+ * notice shall be included in all copies or substantial portions of the Software.
975
+ */
976
+ util.cloneDeep = function cloneDeep(parent, depth, circular, prototype) {
977
+ // maintain two arrays for circular references, where corresponding parents
978
+ // and children have the same index
979
+ var allParents = [];
980
+ var allChildren = [];
981
+
982
+ var useBuffer = typeof Buffer != 'undefined';
983
+
984
+ if (typeof circular === 'undefined')
985
+ circular = true;
986
+
987
+ if (typeof depth === 'undefined')
988
+ depth = 20;
989
+
990
+ // recurse this function so we don't reset allParents and allChildren
991
+ function _clone(parent, depth) {
992
+ // cloning null always returns null
993
+ if (parent === null)
994
+ return null;
995
+
996
+ if (depth === 0)
997
+ return parent;
998
+
999
+ var child;
1000
+ if (typeof parent != 'object') {
1001
+ return parent;
1002
+ }
1003
+
1004
+ if (Utils.isArray(parent)) {
1005
+ child = [];
1006
+ } else if (Utils.isRegExp(parent)) {
1007
+ child = new RegExp(parent.source, util.getRegExpFlags(parent));
1008
+ if (parent.lastIndex) child.lastIndex = parent.lastIndex;
1009
+ } else if (Utils.isDate(parent)) {
1010
+ child = new Date(parent.getTime());
1011
+ } else if (useBuffer && Buffer.isBuffer(parent)) {
1012
+ child = Buffer.alloc(parent.length);
1013
+ parent.copy(child);
1014
+ return child;
1015
+ } else {
1016
+ if (typeof prototype === 'undefined') child = Object.create(Object.getPrototypeOf(parent));
1017
+ else child = Object.create(prototype);
1018
+ }
1019
+
1020
+ if (circular) {
1021
+ var index = allParents.indexOf(parent);
1022
+
1023
+ if (index != -1) {
1024
+ return allChildren[index];
1025
+ }
1026
+ allParents.push(parent);
1027
+ allChildren.push(child);
1028
+ }
1029
+
1030
+ for (var i in parent) {
1031
+ var propDescriptor = Object.getOwnPropertyDescriptor(parent,i);
1032
+ var hasGetter = ((propDescriptor !== undefined) && (propDescriptor.get !== undefined));
1033
+
1034
+ if (hasGetter){
1035
+ Object.defineProperty(child,i,propDescriptor);
1036
+ } else if (util.isPromise(parent[i])) {
1037
+ child[i] = parent[i];
1038
+ } else {
1039
+ child[i] = _clone(parent[i], depth - 1);
1040
+ }
1041
+ }
1042
+
1043
+ return child;
1044
+ }
1045
+
1046
+ return _clone(parent, depth);
1047
+ };
1048
+
1049
+ /**
1050
+ * Set objects given a path as a string list
1051
+ *
1052
+ * @protected
1053
+ * @method setPath
1054
+ * @param object {object} - Object to set the property on
1055
+ * @param path {array[string]} - Array path to the property
1056
+ * @param value {*} - value to set, ignoring null
1057
+ */
1058
+ util.setPath = function (object, path, value) {
1059
+ var nextKey = null;
1060
+ if (value === null || path.length === 0) {
1061
+ return;
1062
+ }
1063
+ else if (path.length === 1) { // no more keys to make, so set the value
1064
+ object[path.shift()] = value;
1065
+ }
1066
+ else {
1067
+ nextKey = path.shift();
1068
+ if (!Object.hasOwnProperty.call(object, nextKey)) {
1069
+ object[nextKey] = {};
1070
+ }
1071
+ util.setPath(object[nextKey], path, value);
1072
+ }
1073
+ };
1074
+
1075
+ /**
1076
+ * Create a new object patterned after substitutionMap, where:
1077
+ * 1. Terminal string values in substitutionMap are used as keys
1078
+ * 2. To look up values in a key-value store, variables
1079
+ * 3. And parent keys are created as necessary to retain the structure of substitutionMap.
1080
+ *
1081
+ * @protected
1082
+ * @method substituteDeep
1083
+ * @param substitutionMap {object} - an object whose terminal (non-subobject) values are strings
1084
+ * @param variables {object[string:value]} - usually process.env, a flat object used to transform
1085
+ * terminal values in a copy of substitutionMap.
1086
+ * @returns {object} - deep copy of substitutionMap with only those paths whose terminal values
1087
+ * corresponded to a key in `variables`
1088
+ */
1089
+ util.substituteDeep = function (substitutionMap, variables) {
1090
+ var result = {};
1091
+
1092
+ function _substituteVars(map, vars, pathTo) {
1093
+ for (var prop in map) {
1094
+ var value = map[prop];
1095
+ if (typeof(value) === 'string') { // We found a leaf variable name
1096
+ if (vars[value] !== undefined && vars[value] !== '') { // if the vars provide a value set the value in the result map
1097
+ util.setPath(result, pathTo.concat(prop), vars[value]);
1098
+ }
1099
+ }
1100
+ else if (util.isObject(value)) { // work on the subtree, giving it a clone of the pathTo
1101
+ if ('__name' in value && '__format' in value && vars[value.__name] !== undefined && vars[value.__name] !== '') {
1102
+ try {
1103
+ var parsedValue = util.parseString(vars[value.__name], value.__format);
1104
+ } catch(err) {
1105
+ err.message = '__format parser error in ' + value.__name + ': ' + err.message;
1106
+ throw err;
1107
+ }
1108
+ util.setPath(result, pathTo.concat(prop), parsedValue);
1109
+ } else {
1110
+ _substituteVars(value, vars, pathTo.concat(prop));
1111
+ }
1112
+ }
1113
+ else {
1114
+ msg = "Illegal key type for substitution map at " + pathTo.join('.') + ': ' + typeof(value);
1115
+ throw Error(msg);
1116
+ }
1117
+ }
1118
+ }
1119
+
1120
+ _substituteVars(substitutionMap, variables, []);
1121
+ return result;
1122
+
1123
+ };
1124
+
1125
+ /* Map environment variables into the configuration if a mapping file,
1126
+ * `custom-environment-variables.EXT` exists.
1127
+ *
1128
+ * @protected
1129
+ * @method getCustomEnvVars
1130
+ * @param configDir {string} - the passed configuration directory
1131
+ * @param extNames {Array[string]} - acceptable configuration file extension names.
1132
+ * @returns {object} - mapped environment variables or {} if there are none
1133
+ */
1134
+ util.getCustomEnvVars = function (configDir, extNames) {
1135
+ var result = {};
1136
+ var resolutionIndex = 1;
1137
+ var allowedFiles = {};
1138
+ extNames.forEach(function (extName) {
1139
+ allowedFiles['custom-environment-variables' + '.' + extName] = resolutionIndex++;
1140
+ });
1141
+ var locatedFiles = util.locateMatchingFiles(configDir, allowedFiles);
1142
+ locatedFiles.forEach(function (fullFilename) {
1143
+ var configObj = util.parseFile(fullFilename);
1144
+ if (configObj) {
1145
+ var environmentSubstitutions = util.substituteDeep(configObj, process.env);
1146
+ util.extendDeep(result, environmentSubstitutions);
1147
+ }
1148
+ });
1149
+ return result;
1150
+ };
1151
+
1152
+ /**
1153
+ * Return true if two objects have equal contents.
1154
+ *
1155
+ * @protected
1156
+ * @method equalsDeep
1157
+ * @param object1 {object} The object to compare from
1158
+ * @param object2 {object} The object to compare with
1159
+ * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1160
+ * @return {boolean} True if both objects have equivalent contents
1161
+ */
1162
+ util.equalsDeep = function(object1, object2, depth) {
1163
+
1164
+ // Recursion detection
1165
+ var t = this;
1166
+ depth = (depth === null ? DEFAULT_CLONE_DEPTH : depth);
1167
+ if (depth < 0) {
1168
+ return {};
1169
+ }
1170
+
1171
+ // Fast comparisons
1172
+ if (!object1 || !object2) {
1173
+ return false;
1174
+ }
1175
+ if (object1 === object2) {
1176
+ return true;
1177
+ }
1178
+ if (typeof(object1) != 'object' || typeof(object2) != 'object') {
1179
+ return false;
1180
+ }
1181
+
1182
+ // They must have the same keys. If their length isn't the same
1183
+ // then they're not equal. If the keys aren't the same, the value
1184
+ // comparisons will fail.
1185
+ if (Object.keys(object1).length != Object.keys(object2).length) {
1186
+ return false;
1187
+ }
1188
+
1189
+ // Compare the values
1190
+ for (var prop in object1) {
1191
+
1192
+ // Call recursively if an object or array
1193
+ if (object1[prop] && typeof(object1[prop]) === 'object') {
1194
+ if (!util.equalsDeep(object1[prop], object2[prop], depth - 1)) {
1195
+ return false;
1196
+ }
1197
+ }
1198
+ else {
1199
+ if (object1[prop] !== object2[prop]) {
1200
+ return false;
1201
+ }
1202
+ }
1203
+ }
1204
+
1205
+ // Test passed.
1206
+ return true;
1207
+ };
1208
+
1209
+ /**
1210
+ * Returns an object containing all elements that differ between two objects.
1211
+ * <p>
1212
+ * This method was designed to be used to create the runtime.json file
1213
+ * contents, but can be used to get the diffs between any two Javascript objects.
1214
+ * </p>
1215
+ * <p>
1216
+ * It works best when object2 originated by deep copying object1, then
1217
+ * changes were made to object2, and you want an object that would give you
1218
+ * the changes made to object1 which resulted in object2.
1219
+ * </p>
1220
+ *
1221
+ * @protected
1222
+ * @method diffDeep
1223
+ * @param object1 {object} The base object to compare to
1224
+ * @param object2 {object} The object to compare with
1225
+ * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1226
+ * @return {object} A differential object, which if extended onto object1 would
1227
+ * result in object2.
1228
+ */
1229
+ util.diffDeep = function(object1, object2, depth) {
1230
+
1231
+ // Recursion detection
1232
+ var t = this, diff = {};
1233
+ depth = (depth === null ? DEFAULT_CLONE_DEPTH : depth);
1234
+ if (depth < 0) {
1235
+ return {};
1236
+ }
1237
+
1238
+ // Process each element from object2, adding any element that's different
1239
+ // from object 1.
1240
+ for (var parm in object2) {
1241
+ var value1 = object1[parm];
1242
+ var value2 = object2[parm];
1243
+ if (value1 && value2 && util.isObject(value2)) {
1244
+ if (!(util.equalsDeep(value1, value2))) {
1245
+ diff[parm] = util.diffDeep(value1, value2, depth - 1);
1246
+ }
1247
+ }
1248
+ else if (Array.isArray(value1) && Array.isArray(value2)) {
1249
+ if(!util.equalsDeep(value1, value2)) {
1250
+ diff[parm] = value2;
1251
+ }
1252
+ }
1253
+ else if (value1 !== value2){
1254
+ diff[parm] = value2;
1255
+ }
1256
+ }
1257
+
1258
+ // Return the diff object
1259
+ return diff;
1260
+
1261
+ };
1262
+
1263
+ /**
1264
+ * Extend an object, and any object it contains.
1265
+ *
1266
+ * This does not replace deep objects, but dives into them
1267
+ * replacing individual elements instead.
1268
+ *
1269
+ * @protected
1270
+ * @method extendDeep
1271
+ * @param mergeInto {object} The object to merge into
1272
+ * @param mergeFrom... {object...} - Any number of objects to merge from
1273
+ * @param depth {integer} An optional depth to prevent recursion. Default: 20.
1274
+ * @return {object} The altered mergeInto object is returned
1275
+ */
1276
+ util.extendDeep = function(mergeInto) {
1277
+
1278
+ // Initialize
1279
+ var t = this;
1280
+ var vargs = Array.prototype.slice.call(arguments, 1);
1281
+ var depth = vargs.pop();
1282
+ if (typeof(depth) != 'number') {
1283
+ vargs.push(depth);
1284
+ depth = DEFAULT_CLONE_DEPTH;
1285
+ }
1286
+
1287
+ // Recursion detection
1288
+ if (depth < 0) {
1289
+ return mergeInto;
1290
+ }
1291
+
1292
+ // Cycle through each object to extend
1293
+ vargs.forEach(function(mergeFrom) {
1294
+
1295
+ // Cycle through each element of the object to merge from
1296
+ for (var prop in mergeFrom) {
1297
+
1298
+ // save original value in deferred elements
1299
+ var fromIsDeferredFunc = mergeFrom[prop] instanceof DeferredConfig;
1300
+ var isDeferredFunc = mergeInto[prop] instanceof DeferredConfig;
1301
+
1302
+ if (fromIsDeferredFunc && Object.hasOwnProperty.call(mergeInto, prop)) {
1303
+ mergeFrom[prop]._original = isDeferredFunc ? mergeInto[prop]._original : mergeInto[prop];
1304
+ }
1305
+ // Extend recursively if both elements are objects and target is not really a deferred function
1306
+ if (mergeFrom[prop] instanceof Date) {
1307
+ mergeInto[prop] = mergeFrom[prop];
1308
+ } if (mergeFrom[prop] instanceof RegExp) {
1309
+ mergeInto[prop] = mergeFrom[prop];
1310
+ } else if (util.isObject(mergeInto[prop]) && util.isObject(mergeFrom[prop]) && !isDeferredFunc) {
1311
+ util.extendDeep(mergeInto[prop], mergeFrom[prop], depth - 1);
1312
+ }
1313
+ else if (util.isPromise(mergeFrom[prop])) {
1314
+ mergeInto[prop] = mergeFrom[prop];
1315
+ }
1316
+ // Copy recursively if the mergeFrom element is an object (or array or fn)
1317
+ else if (mergeFrom[prop] && typeof mergeFrom[prop] === 'object') {
1318
+ mergeInto[prop] = util.cloneDeep(mergeFrom[prop], depth -1);
1319
+ }
1320
+
1321
+ // Copy property descriptor otherwise, preserving accessors
1322
+ else if (Object.getOwnPropertyDescriptor(Object(mergeFrom), prop)){
1323
+ Object.defineProperty(mergeInto, prop, Object.getOwnPropertyDescriptor(Object(mergeFrom), prop));
1324
+ } else {
1325
+ mergeInto[prop] = mergeFrom[prop];
1326
+ }
1327
+ }
1328
+ });
1329
+
1330
+ // Chain
1331
+ return mergeInto;
1332
+
1333
+ };
1334
+
1335
+ /**
1336
+ * Is the specified argument a regular javascript object?
1337
+ *
1338
+ * The argument is an object if it's a JS object, but not an array.
1339
+ *
1340
+ * @protected
1341
+ * @method isObject
1342
+ * @param obj {*} An argument of any type.
1343
+ * @return {boolean} TRUE if the arg is an object, FALSE if not
1344
+ */
1345
+ util.isObject = function(obj) {
1346
+ return (obj !== null) && (typeof obj === 'object') && !(Array.isArray(obj));
1347
+ };
1348
+
1349
+ /**
1350
+ * Is the specified argument a javascript promise?
1351
+ *
1352
+ * @protected
1353
+ * @method isPromise
1354
+ * @param obj {*} An argument of any type.
1355
+ * @returns {boolean}
1356
+ */
1357
+ util.isPromise = function(obj) {
1358
+ return Object.prototype.toString.call(obj) === '[object Promise]';
1359
+ };
1360
+
1361
+ /**
1362
+ * <p>Initialize a parameter from the command line or process environment</p>
1363
+ *
1364
+ * <p>
1365
+ * This method looks for the parameter from the command line in the format
1366
+ * --PARAMETER=VALUE, then from the process environment, then from the
1367
+ * default specified as an argument.
1368
+ * </p>
1369
+ *
1370
+ * @method initParam
1371
+ * @param paramName {String} Name of the parameter
1372
+ * @param [defaultValue] {Any} Default value of the parameter
1373
+ * @return {Any} The found value, or default value
1374
+ */
1375
+ util.initParam = function (paramName, defaultValue) {
1376
+ var t = this;
1377
+
1378
+ // Record and return the value
1379
+ var value = util.getCmdLineArg(paramName) || process.env[paramName] || defaultValue;
1380
+ env[paramName] = value;
1381
+ return value;
1382
+ }
1383
+
1384
+ /**
1385
+ * <p>Get Command Line Arguments</p>
1386
+ *
1387
+ * <p>
1388
+ * This method allows you to retrieve the value of the specified command line argument.
1389
+ * </p>
1390
+ *
1391
+ * <p>
1392
+ * The argument is case sensitive, and must be of the form '--ARG_NAME=value'
1393
+ * </p>
1394
+ *
1395
+ * @method getCmdLineArg
1396
+ * @param searchFor {String} The argument name to search for
1397
+ * @return {*} false if the argument was not found, the argument value if found
1398
+ */
1399
+ util.getCmdLineArg = function (searchFor) {
1400
+ var cmdLineArgs = process.argv.slice(2, process.argv.length),
1401
+ argName = '--' + searchFor + '=';
1402
+
1403
+ for (var argvIt = 0; argvIt < cmdLineArgs.length; argvIt++) {
1404
+ if (cmdLineArgs[argvIt].indexOf(argName) === 0) {
1405
+ return cmdLineArgs[argvIt].substr(argName.length);
1406
+ }
1407
+ }
1408
+
1409
+ return false;
1410
+ }
1411
+
1412
+ /**
1413
+ * <p>Get a Config Environment Variable Value</p>
1414
+ *
1415
+ * <p>
1416
+ * This method returns the value of the specified config environment variable,
1417
+ * including any defaults or overrides.
1418
+ * </p>
1419
+ *
1420
+ * @method getEnv
1421
+ * @param varName {String} The environment variable name
1422
+ * @return {String} The value of the environment variable
1423
+ */
1424
+ util.getEnv = function (varName) {
1425
+ return env[varName];
1426
+ }
1427
+
1428
+
1429
+
1430
+ /**
1431
+ * Returns a string of flags for regular expression `re`.
1432
+ *
1433
+ * @param {RegExp} re Regular expression
1434
+ * @returns {string} Flags
1435
+ */
1436
+ util.getRegExpFlags = function (re) {
1437
+ var flags = '';
1438
+ re.global && (flags += 'g');
1439
+ re.ignoreCase && (flags += 'i');
1440
+ re.multiline && (flags += 'm');
1441
+ return flags;
1442
+ };
1443
+
1444
+ /**
1445
+ * Returns a new deep copy of the current config object, or any part of the config if provided.
1446
+ *
1447
+ * @param {Object} config The part of the config to copy and serialize. Omit this argument to return the entire config.
1448
+ * @returns {Object} The cloned config or part of the config
1449
+ */
1450
+ util.toObject = function(config) {
1451
+ return JSON.parse(JSON.stringify(config || this));
1452
+ };
1453
+
1454
+ // Run strictness checks on NODE_ENV and NODE_APP_INSTANCE and throw an error if there's a problem.
1455
+ util.runStrictnessChecks = function (config) {
1456
+ var sources = config.util.getConfigSources();
1457
+
1458
+ var sourceFilenames = sources.map(function (src) {
1459
+ return Path.basename(src.name);
1460
+ });
1461
+
1462
+ NODE_ENV.forEach(function(env) {
1463
+ // Throw an exception if there's no explicit config file for NODE_ENV
1464
+ var anyFilesMatchEnv = sourceFilenames.some(function (filename) {
1465
+ return filename.match(env);
1466
+ });
1467
+ // development is special-cased because it's the default value
1468
+ if (env && (env !== 'development') && !anyFilesMatchEnv) {
1469
+ _warnOrThrow(NODE_ENV_VAR_NAME+" value of '"+env+"' did not match any deployment config file names.");
1470
+ }
1471
+ // Throw if NODE_ENV matches' default' or 'local'
1472
+ if ((env === 'default') || (env === 'local')) {
1473
+ _warnOrThrow(NODE_ENV_VAR_NAME+" value of '"+env+"' is ambiguous.");
1474
+ }
1475
+ });
1476
+
1477
+ // Throw an exception if there's no explicit config file for NODE_APP_INSTANCE
1478
+ var anyFilesMatchInstance = sourceFilenames.some(function (filename) {
1479
+ return filename.match(APP_INSTANCE);
1480
+ });
1481
+ if (APP_INSTANCE && !anyFilesMatchInstance) {
1482
+ _warnOrThrow("NODE_APP_INSTANCE value of '"+APP_INSTANCE+"' did not match any instance config file names.");
1483
+ }
1484
+
1485
+ function _warnOrThrow (msg) {
1486
+ var beStrict = process.env.NODE_CONFIG_STRICT_MODE;
1487
+ var prefix = beStrict ? 'FATAL: ' : 'WARNING: ';
1488
+ var seeURL = 'See https://github.com/node-config/node-config/wiki/Strict-Mode';
1489
+
1490
+ console.error(prefix+msg);
1491
+ console.error(prefix+seeURL);
1492
+
1493
+ // Accept 1 and true as truthy values. When set via process.env, Node.js casts them to strings.
1494
+ if (["true", "1"].indexOf(beStrict) >= 0) {
1495
+ throw new Error(prefix+msg+' '+seeURL);
1496
+ }
1497
+ }
1498
+ };
1499
+
1500
+ // Helper functions shared accross object members
1501
+ function _toAbsolutePath (configDir) {
1502
+ if (configDir.indexOf('.') === 0) {
1503
+ return Path.join(process.cwd(), configDir);
1504
+ }
1505
+
1506
+ return configDir;
1507
+ }
1508
+
1509
+ // Instantiate and export the configuration
1510
+ var config = module.exports = new Config();
1511
+
1512
+ // copy methods to util for backwards compatibility
1513
+ util.stripComments = Parser.stripComments;
1514
+ util.stripYamlComments = Parser.stripYamlComments;
1515
+
1516
+ // Produce warnings if the configuration is empty
1517
+ var showWarnings = !(util.initParam('SUPPRESS_NO_CONFIG_WARNING'));
1518
+ if (showWarnings && Object.keys(config).length === 0) {
1519
+ console.error('WARNING: No configurations found in configuration directory:' +CONFIG_DIR);
1520
+ console.error('WARNING: To disable this warning set SUPPRESS_NO_CONFIG_WARNING in the environment.');
1521
+ }