harthat-cookie 3.1.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of harthat-cookie 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
+ }