Comparing version 0.1.0 to 1.0.0
266
index.js
@@ -0,5 +1,19 @@ | ||
/*───────────────────────────────────────────────────────────────────────────*\ | ||
│ Copyright (C) 2014 eBay Software Foundation │ | ||
│ │ | ||
│ Licensed under the Apache License, Version 2.0 (the "License"); │ | ||
│ you may not use this file except in compliance with the License. │ | ||
│ You may obtain a copy of the License at │ | ||
│ │ | ||
│ http://www.apache.org/licenses/LICENSE-2.0 │ | ||
│ │ | ||
│ Unless required by applicable law or agreed to in writing, software │ | ||
│ distributed under the License is distributed on an "AS IS" BASIS, │ | ||
│ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ | ||
│ See the License for the specific language governing permissions and │ | ||
│ limitations under the License. │ | ||
\*───────────────────────────────────────────────────────────────────────────*/ | ||
'use strict'; | ||
var path = require('path'); | ||
var nconf = require('nconf'); | ||
var shush = require('shush'); | ||
@@ -9,110 +23,100 @@ var caller = require('caller'); | ||
var shortstop = require('shortstop'); | ||
var common = require('./lib/common'); | ||
var provider = require('./lib/provider'); | ||
var debug = require('debuglog')('confit'); | ||
var env = require('./lib/env'); | ||
var util = require('./lib/util'); | ||
/** | ||
* Initializes environment convenience props in the provided nconf provider. | ||
* @param config an nconf Provider. | ||
* @returns {Object} the newly configured nconf Provider. | ||
*/ | ||
function environment(nodeEnv) { | ||
var data = {}; | ||
function config(store) { | ||
return { | ||
debug('NODE_ENV set to \'%s\'', nodeEnv); | ||
get: function get(key) { | ||
var obj; | ||
// Normalize env and set convenience values. | ||
Object.keys(env).forEach(function (current) { | ||
var match; | ||
if (thing.isString(key) && key.length) { | ||
match = env[current].test(nodeEnv); | ||
if (match) { nodeEnv = current; } | ||
key = key.split(':'); | ||
obj = store; | ||
data[current] = match; | ||
}); | ||
while (obj && key.length) { | ||
if (obj.constructor !== Object) { | ||
// Do not allow traversal into complex types, | ||
// such as Buffer, Date, etc. So, this type | ||
// of key will fail: 'foo:mystring:length' | ||
return undefined; | ||
} | ||
obj = obj[key.shift()]; | ||
} | ||
debug('env:env set to \'%s\'', nodeEnv); | ||
return obj; | ||
// Set (or re-set) env:{nodeEnv} value in case | ||
// NODE_ENV was not one of our predetermined env | ||
// keys (so `config.get('env:blah')` will be true). | ||
data[nodeEnv] = true; | ||
data.env = nodeEnv; | ||
return { env: data }; | ||
} | ||
} | ||
return undefined; | ||
}, | ||
/** | ||
* Creates a local nconf provider instance. NO GLOBAL! | ||
* @returns {Object} an nconf provider | ||
*/ | ||
function provider() { | ||
var config; | ||
set: function set(key, value) { | ||
var obj, prop; | ||
config = new nconf.Provider(); | ||
config.add('argv'); | ||
config.add('env'); | ||
if (thing.isString(key) && key.length) { | ||
// Put override before memory to ensure env | ||
// values are immutable. | ||
config.overrides({ | ||
type: 'literal', | ||
store: environment(config.get('NODE_ENV') || 'development') | ||
}); | ||
key = key.split(':'); | ||
obj = store; | ||
config.add('memory'); | ||
while (key.length - 1) { | ||
prop = key.shift(); | ||
return config; | ||
} | ||
// Create new object for property, if nonexistent | ||
if (!obj.hasOwnProperty(prop)) { | ||
obj[prop] = {}; | ||
} | ||
obj = obj[prop]; | ||
if (obj && obj.constructor !== Object) { | ||
// Do not allow traversal into complex types, | ||
// such as Buffer, Date, etc. So, this type | ||
// of key will fail: 'foo:mystring:length' | ||
return undefined; | ||
} | ||
} | ||
/** | ||
* Creates a file loader that uses the provided `basedir`. | ||
* @param basedir the root directory against which file paths will be resolved. | ||
* @returns {Function} the file loader implementation. | ||
*/ | ||
function loader(basedir) { | ||
return (obj[key.shift()] = value); | ||
} | ||
return function load(file) { | ||
var name, config; | ||
return undefined; | ||
}, | ||
name = path.basename(file, path.extname(file)); | ||
config = path.join(basedir, file); | ||
use: function use(obj) { | ||
common.merge(obj, store); | ||
} | ||
return { | ||
name: name, | ||
data: shush(config) | ||
}; | ||
}; | ||
} | ||
/** | ||
* Wraps the provided nconf Provider in a simpler convenience API. | ||
* @param config an nconf Provider. | ||
*/ | ||
function wrap(config) { | ||
function builder(options) { | ||
return { | ||
get: function get(key) { | ||
return config.get(key); | ||
}, | ||
_store: {}, | ||
set: function set(key, value) { | ||
// NOTE: There was discussion around potentially warning | ||
// on attempts to set immutable values. The would require | ||
// a minimum of one additional operation, which was deemed | ||
// overkill for a small/unlikely scenrio. Can revisit. | ||
config.set(key, value); | ||
addOverride: function addOverride(file) { | ||
file = common.isAbsolute(file) ? file : path.join(options.basedir, file); | ||
common.merge(shush(file), this._store); | ||
return this; | ||
}, | ||
use: function use(obj) { | ||
// Merge into memory store. | ||
// This must be done b/c nconf applies things kind of backward. | ||
// If we just used a literal store it would get added to the END | ||
// so no values would be overridden. Additionally, only the memory | ||
// store is writable at this point so all updates live there. | ||
config.merge(obj); | ||
create: function create(callback) { | ||
var shorty; | ||
shorty = shortstop.create(); | ||
Object.keys(options.protocols).forEach(function (protocol) { | ||
shorty.use(protocol, options.protocols[protocol]); | ||
}); | ||
shorty.resolve(this._store, function (err, data) { | ||
if (err) { | ||
callback(err); | ||
return; | ||
} | ||
callback(null, config(data)); | ||
}); | ||
} | ||
@@ -124,18 +128,31 @@ | ||
/** | ||
* Main module entrypoint. Creates a confit config object using the provided | ||
* options. | ||
* @param options the configuration settings for this config instance. | ||
* @param callback the function to which error or config object will be passed. | ||
*/ | ||
module.exports = function confit(options, callback) { | ||
var shorty, config, tasks, load; | ||
function possibly(resolve, reject) { | ||
return function maybe() { | ||
try { | ||
return resolve.apply(null, arguments); | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}; | ||
} | ||
// Normalize arguments | ||
if (thing.isFunction(options)) { | ||
callback = options; | ||
options = undefined; | ||
function resolve(file, store) { | ||
return common.merge(shush(file), store); | ||
} | ||
function reject(err) { | ||
if (err.code && err.code === 'MODULE_NOT_FOUND') { | ||
debug('WARNING:', err.message); | ||
return; | ||
} | ||
throw err; | ||
} | ||
// ... still normalizing | ||
module.exports = function confit(options) { | ||
var factory, margeFile, file; | ||
// Normalize arguments | ||
if (thing.isString(options)) { | ||
@@ -149,56 +166,21 @@ options = { basedir: options }; | ||
options.basedir = options.basedir || path.dirname(caller()); | ||
options.protocols = options.protocols || {}; | ||
factory = builder(options); | ||
common.merge(provider.argv(), factory._store); | ||
common.merge(provider.env(), factory._store); | ||
common.merge(provider.convenience(), factory._store); | ||
// Configure shortstop using provided protocols | ||
shorty = shortstop.create(); | ||
if (thing.isObject(options.protocols)) { | ||
Object.keys(options.protocols).forEach(function (protocol) { | ||
shorty.use(protocol, options.protocols[protocol]); | ||
}); | ||
} | ||
// Backdoor a couple files before we get going. | ||
margeFile = possibly(resolve, reject); | ||
// Create config provider and initialize basedir | ||
// TODO: Add basedir to overrides so it's readonly? | ||
config = provider(); | ||
config.set('basedir', options.basedir); | ||
// File 1: The default config file. | ||
file = path.join(options.basedir, options.defaults); | ||
margeFile(file, factory._store); | ||
// File 2: The env-specific config file. | ||
file = path.join(options.basedir, factory._store.env.env + '.json'); | ||
margeFile(file, factory._store); | ||
tasks = []; | ||
load = loader(options.basedir); | ||
// Load the env-specific config file as a literal | ||
// datastore. Can't use `file` b/c we preprocess it. | ||
tasks.push(function (done) { | ||
var file = load(config.get('env:env') + '.json'); | ||
config.use(file.name, { | ||
type: 'literal', | ||
store: shorty.resolve(file.data) | ||
}); | ||
done(); | ||
}); | ||
// Set defaults from `defaults` file. | ||
tasks.push(function (done) { | ||
var file = load(options.defaults); | ||
config.defaults(shorty.resolve(file.data)); | ||
done(); | ||
}); | ||
util.each(tasks, function (err) { | ||
// XXX: Force async until shortstop@1.0 is integrated. | ||
// Only report unusual errors. MODULE_NOT_FOUND is an | ||
// acceptable scenario b/c no files are truly requried. | ||
if (thing.isObject(err) && err.code !== 'MODULE_NOT_FOUND') { | ||
setImmediate(callback.bind(null, err)); | ||
return; | ||
} | ||
config = wrap(config); | ||
setImmediate(callback.bind(null, null, config)); | ||
}); | ||
return factory; | ||
}; |
{ | ||
"name": "confit", | ||
"version": "0.1.0", | ||
"version": "1.0.0", | ||
"description": "Environment-aware configuration.", | ||
@@ -16,3 +16,3 @@ "main": "index.js", | ||
"type": "git", | ||
"url": "git://github.com/totherik/confit.git" | ||
"url": "git://github.com/krakenjs/confit.git" | ||
}, | ||
@@ -25,5 +25,9 @@ "keywords": [ | ||
"author": "Erik Toth <totherik@gmail.com>", | ||
"license": "MIT", | ||
"licenses": [ | ||
{ | ||
"type": "Apache 2.0", | ||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html" | ||
} | ||
], | ||
"readmeFilename": "README.md", | ||
"gitHead": "4bc76f5804f42d55cacd5cdbc3661ad1c605b3ca", | ||
"devDependencies": { | ||
@@ -35,10 +39,9 @@ "tape": "~2.10.2", | ||
"dependencies": { | ||
"nconf": "~0.6.9", | ||
"shortstop": "0.0.1", | ||
"caller": "0.0.1", | ||
"minimist": "~0.0.8", | ||
"caller": "~0.0.1", | ||
"core-util-is": "~1.0.1", | ||
"async": "~0.2.10", | ||
"shush": "0.0.1", | ||
"debuglog": "~1.0.1" | ||
"debuglog": "~1.0.1", | ||
"shortstop": "~1.0.0" | ||
} | ||
} |
@@ -9,3 +9,3 @@ # confit | ||
[![Build Status](https://travis-ci.org/totherik/confit.png)](https://travis-ci.org/totherik/confit) | ||
[![Build Status](https://travis-ci.org/krakenjs/confit.png)](https://travis-ci.org/krakenjs/confit) | ||
@@ -17,7 +17,6 @@ ## Usage | ||
### confit(options, callback); | ||
* `options` (*String* | *Object*) - the base directory in which config files live | ||
or a configuration object. | ||
* `callback` (*Function*) - the callback to be called with the error or config object. | ||
Signature `function (err, config) { /* ... */}` | ||
### confit([options]) | ||
* `options` (*String* | *Object*) - the base directory in which config files live or a configuration object. If no | ||
arguments is provided, defaults to the directory of the calling file. Signature `function (err, config) {}` | ||
* returns - config factory. | ||
@@ -31,3 +30,3 @@ ```javascript | ||
var basedir = path.join(__dirname, 'config'); | ||
confit(basedir, function (err, config) { | ||
confit(basedir).create(function (err, config) { | ||
config.get; // Function | ||
@@ -41,2 +40,24 @@ config.set; // Function | ||
### config factory | ||
* `addOverride(filepath)` - Register a file (JSON or JS), the contents of which should be merged with the config datastore. | ||
* `create(callback)` - Creates the config object, ready for use. Callback signature: `function (err, config) {}` | ||
```javascript | ||
// All methods besides `create` are chainable | ||
confit(options) | ||
.addOverride('./mysettings.json') | ||
.addOverride('./mysettings.json') | ||
.create(function (err, config) { | ||
// ... | ||
}); | ||
// - or - | ||
// | ||
// var factory = confit(options); | ||
// factory.addOverride('./mysettings.json'); | ||
// factory.create(function (err, config) { | ||
// // ... | ||
// }); | ||
``` | ||
## Options | ||
@@ -66,8 +87,4 @@ * `basedir` (*String*) - the base directory in which config files can be found. | ||
confit(options, function (err, config) { | ||
config.get; // Function | ||
config.set; // Function | ||
config.use; // Function | ||
config.get('env:env'); // 'development' | ||
confit(options).create(function (err, config) { | ||
// ... | ||
}); | ||
@@ -77,5 +94,5 @@ ``` | ||
## API | ||
* `get(key)` - Retrieve the value for a given key. | ||
* `set(key, value)` - Set a value for the given key. | ||
## Config API | ||
* `get(key)` - Retrieve the value for a given key. Colon-delimited keys can be used to traverse the object hierarchy. | ||
* `set(key, value)` - Set a value for the given key. Colon-delimited keys can be used to traverse the object hierarchy. | ||
* `use(obj)` - merge provided object into config. | ||
@@ -89,2 +106,5 @@ | ||
config.get('foo'); // 'baz' | ||
config.use({ a: { b: { c: 'd' } } } ); | ||
config.get('a:b:c'); // 'd' | ||
``` | ||
@@ -91,0 +111,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
22581
6
11
248
1
131
1
80
3
1
+ Addedminimist@~0.0.8
+ Addedshortstop@1.0.5(transitive)
- Removedasync@~0.2.10
- Removednconf@~0.6.9
- Removedasync@0.2.9(transitive)
- Removedini@1.3.8(transitive)
- Removednconf@0.6.9(transitive)
- Removedoptimist@0.6.0(transitive)
- Removedshortstop@0.0.1(transitive)
- Removedwordwrap@0.0.3(transitive)
Updatedcaller@~0.0.1
Updatedshortstop@~1.0.0