creature-features
Advanced tools
Comparing version 2.1.2 to 3.0.0
@@ -6,3 +6,4 @@ { | ||
"DefaultLocation": true, | ||
"IsThisTheDefaultFile": true | ||
"IsThisTheDefaultFile": true, | ||
"IsCustomLocation": false | ||
} |
@@ -6,3 +6,5 @@ { | ||
"IsDevelopment": true, | ||
"IsTest": false | ||
"IsTest": false, | ||
"IsDefaultLocation": true, | ||
"IsDevelopmentFromDefault": true | ||
} |
122
index.js
@@ -1,15 +0,16 @@ | ||
'use strict'; | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const endWith = require('end-with'); | ||
const random = require('random-weighted'); | ||
const exists = fs.existsSync; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
module.exports = function(config) { | ||
let env; | ||
let featuresFile; | ||
let baseFeatures; | ||
let defaults = { | ||
location: './features/' | ||
}; | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var endWith = require('end-with'); | ||
var exists = require('file-exists'); | ||
module.exports = function (config) { | ||
var env = void 0, | ||
featuresFile = void 0, | ||
baseFeatures = void 0, | ||
overrides = void 0, | ||
locationBase = './features/'; | ||
const settings = Object.assign({}, defaults, config); | ||
if (arguments.length === 0) { | ||
@@ -21,14 +22,16 @@ env = process.env.NODE_ENV || 'development'; | ||
} else { | ||
//config is believed to be an object | ||
env = config.env ? config.env : process.env.NODE_ENV; | ||
overrides = config; | ||
delete overrides.env; | ||
if (overrides.location) { | ||
locationBase = endWith(overrides.location, '/') ? overrides.location : overrides.location + '/'; | ||
} | ||
} | ||
} | ||
featuresFile = '' + locationBase + env + '.json'; | ||
baseFeatures = locationBase + 'default.json'; | ||
featuresFile = `${ | ||
endWith(settings.location, '/') | ||
? settings.location | ||
: settings.location + '/' | ||
}${env}.json`; | ||
baseFeatures = `${ | ||
endWith(settings.location, '/') | ||
? settings.location | ||
: settings.location + '/' | ||
}default.json`; | ||
@@ -43,14 +46,77 @@ var featuresFiles = []; | ||
// look for a "named" development file | ||
var files = fs.readdirSync(path.join(process.cwd(), './features')); | ||
for (var i = 0; i < files.length; i++) { | ||
if (files[i].indexOf('development.') > -1 && files[i] !== 'development.json') { | ||
const files = fs.readdirSync(path.join(process.cwd(), './features')); | ||
for (let i = 0; i < files.length; i++) { | ||
if ( | ||
files[i].indexOf('development.') > -1 && | ||
files[i] !== 'development.json' | ||
) { | ||
featuresFiles.push('./features/' + files[i]); | ||
} | ||
}; | ||
} | ||
} | ||
var requiredFeatures = featuresFiles.map(function (file) { | ||
return require(file); | ||
const requiredFeatures = featuresFiles | ||
.filter(f => exists(f)) | ||
.map(function(file) { | ||
return require(file); | ||
}); | ||
const feats = requiredFeatures.reduce((prev, current) => { | ||
return { ...prev, ...current }; | ||
}, {}); | ||
const featureProxy = new Proxy(feats, { | ||
get: (obj, prop) => { | ||
if (obj[prop] === undefined) { | ||
throw new Error(`${prop} feature is undefined`); | ||
} | ||
if (obj[prop] === true || obj[prop] === false) { | ||
return obj[prop]; | ||
} | ||
if (obj[prop].range !== undefined) { | ||
//validate | ||
const total = obj[prop].range.reduce( | ||
(previous, current) => previous + current, | ||
0 | ||
); | ||
if (total !== 100) { | ||
throw new Error('Range values must total 100'); | ||
} | ||
const range = obj[prop].range.map(v => v / 100); | ||
return random(range) === 0; | ||
} | ||
return (...args) => | ||
Reflect.apply( | ||
(...args) => { | ||
if (args.length === 2 && typeof args[1] === 'string') { | ||
const check = ` | ||
'use strict'; | ||
const ${obj[prop].parameters} = '${args[1]}' | ||
return ${obj[prop].check}`; | ||
const funced = new Function(check); | ||
const result = funced(); | ||
return result; | ||
} | ||
const check = ` | ||
'use strict'; | ||
const { ${obj[prop].parameters} } = ${JSON.stringify(args[1])} | ||
return ${obj[prop].check}`; | ||
const funced = new Function(check); | ||
const result = funced(); | ||
return result; | ||
}, | ||
featureProxy, | ||
[prop, ...args] | ||
); | ||
}, | ||
set: () => { | ||
throw new Error('Not allowed to set a value'); | ||
} | ||
}); | ||
return Object.assign.apply(Object, _toConsumableArray(requiredFeatures)); | ||
}; | ||
return featureProxy; | ||
}; |
{ | ||
"name": "creature-features", | ||
"version": "2.1.2", | ||
"version": "3.0.0", | ||
"description": "Feature flags for node.js", | ||
"main": "index.js", | ||
"engines": { | ||
"node": ">=10.0.0" | ||
}, | ||
"scripts": { | ||
"build": "babel src -d .", | ||
"pretest": "npm run build", | ||
"test": "ava test/*.js", | ||
"cov": "cross-env NODE_ENV=test nyc ava test/*.js" | ||
"lint": "eslint ./index.js ./test/*.test.js", | ||
"test": "jest test/*.test.js", | ||
"cov": "jest test/*.test.js --coverage" | ||
}, | ||
@@ -19,6 +21,5 @@ "repository": { | ||
"flags", | ||
"features", | ||
"toggles", | ||
"feature", | ||
"toggles", | ||
"feature flags", | ||
"feature toggles", | ||
"continuous delivery", | ||
@@ -34,13 +35,16 @@ "continuous integration" | ||
"devDependencies": { | ||
"ava": "^0.17.0", | ||
"babel-cli": "^6.22.2", | ||
"babel-preset-env": "^1.1.8", | ||
"cross-env": "^3.1.3", | ||
"nyc": "^10.0.0", | ||
"tape": "^4.6.3" | ||
"cross-env": "^5.2.0", | ||
"eslint": "^5.6.0", | ||
"eslint-config-prettier": "^3.1.0", | ||
"eslint-plugin-node": "^7.0.1", | ||
"eslint-plugin-prettier": "^2.6.2", | ||
"jest-cli": "^23.6.0", | ||
"prettier": "^1.14.3" | ||
}, | ||
"dependencies": { | ||
"end-with": "^1.0.2", | ||
"file-exists": "^2.0.0" | ||
"file-exists": "^5.0.1", | ||
"random-weighted": "^1.0.0", | ||
"weighted": "^0.3.0" | ||
} | ||
} |
# Creature Features | ||
## Dead simple feature flags for node.js | ||
### Install | ||
@@ -15,3 +16,3 @@ ```bash | ||
-- test.json (settings for test) | ||
-- production.json (settigns for production) | ||
-- production.json (settigns for production) | ||
-- {any other environment}.json | ||
@@ -31,3 +32,3 @@ ``` | ||
const features = require('creature-features')(); | ||
// Default behavior | ||
if (features.FeatureOne) { | ||
@@ -39,2 +40,58 @@ // do something new | ||
``` | ||
#### Rules Based | ||
```json | ||
{ | ||
"FeatureOne": true, | ||
"FeatureTwo": true, | ||
"RuleForEmailFeature": { | ||
"parameters": "email", | ||
"check": "email.indexOf('test') > -1" | ||
}, | ||
"RuleForEmailAndRoleFeature": { | ||
"parameters": "email,role", | ||
"check": "email.indexOf('test') > -1 && role ==='admin'" | ||
} | ||
} | ||
``` | ||
```javascript | ||
const features = require('creature-features')(); | ||
// Default behavior | ||
if (features.RuleForEmailFeature(user.email)) { | ||
// do something new | ||
} else { | ||
// do the old thing | ||
} | ||
if (features.RuleForEmailAndRoleFeature({ email: user.email, role: user.account.role })) { | ||
// do something new | ||
} else { | ||
// do the old thing | ||
} | ||
``` | ||
#### Weight Based | ||
```json | ||
{ | ||
"FeatureOne": true, | ||
"FeatureTwo": true, | ||
"PercentBasedFiftyFifty": { | ||
"range": [50, 50] | ||
}, | ||
"PercentBasedOneThird": { | ||
"range": [33, 67] | ||
}, | ||
} | ||
``` | ||
```javascript | ||
const features = require('creature-features')(); | ||
// Roughly 50% of the the time this will be true | ||
if (features.PercentBasedFiftyFifty) { | ||
// do something new | ||
} | ||
// Roughly 33% of the the time this will be true | ||
if (features.PercentBasedOneThird) { | ||
// do something new | ||
} | ||
``` | ||
### In Webpack | ||
@@ -41,0 +98,0 @@ ```javascript |
@@ -6,3 +6,5 @@ { | ||
"LocationIsTest": true, | ||
"DevelopmentFeature": true | ||
"DevelopmentFeature": true, | ||
"IsDefaultLocation": false, | ||
"IsDevelopmentFromDefault": false | ||
} |
Sorry, the diff of this file is not supported yet
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
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
19436
20
473
122
21
4
7
3
+ Addedrandom-weighted@^1.0.0
+ Addedweighted@^0.3.0
+ Addedfile-exists@5.0.1(transitive)
+ Addedrandom-weighted@1.0.0(transitive)
+ Addedweighted@0.3.0(transitive)
- Removedfile-exists@2.0.0(transitive)
Updatedfile-exists@^5.0.1