Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

creature-features

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

creature-features - npm Package Compare versions

Comparing version 3.1.0 to 3.1.2

.circleci/config.yml

36

.eslintrc.json

@@ -7,4 +7,10 @@ {

},
"extends": ["eslint:recommended", "plugin:node/recommended", "prettier"],
"plugins": ["prettier"],
"extends": [
"eslint:recommended",
"plugin:node/recommended",
"prettier"
],
"plugins": [
"prettier"
],
"parserOptions": {

@@ -17,3 +23,2 @@ "ecmaVersion": 2018,

},
"rules": {

@@ -27,7 +32,21 @@ "indent": [

],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "always"],
"no-console": [0],
"node/exports-style": ["error", "module.exports"],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"never"
],
"no-console": [
0
],
"node/exports-style": [
"error",
"module.exports"
],
"prettier/prettier": [

@@ -39,2 +58,3 @@ "error",

"jsxBracketSameLine": true,
"semi": false,
"parser": "flow"

@@ -41,0 +61,0 @@ }

@@ -1,24 +0,24 @@

const path = require('path');
const fs = require('fs');
const debug = require('debug')('Creature-Features');
const endWith = require('end-with');
const random = require('random-weighted');
const exists = fs.existsSync;
const path = require('path')
const fs = require('fs')
const debug = require('debug')('Creature-Features')
const endWith = require('end-with')
const random = require('random-weighted')
const exists = fs.existsSync
module.exports = function(config) {
let env;
let featuresFile;
let baseFeatures;
let env
let featuresFile
let baseFeatures
let defaults = {
location: './features/'
};
}
const settings = Object.assign({}, defaults, config);
const settings = Object.assign({}, defaults, config)
if (arguments.length === 0) {
env = process.env.NODE_ENV || 'development';
env = process.env.NODE_ENV || 'development'
} else {
if (typeof config === 'string') {
env = config;
env = config
} else {
env = config.env ? config.env : process.env.NODE_ENV;
env = config.env ? config.env : process.env.NODE_ENV
}

@@ -31,3 +31,3 @@ }

: settings.location + '/'
}${env}.json`;
}${env}.json`
baseFeatures = `${

@@ -37,8 +37,8 @@ endWith(settings.location, '/')

: settings.location + '/'
}default.json`;
}default.json`
var featuresFiles = [];
featuresFiles.push(baseFeatures);
var featuresFiles = []
featuresFiles.push(baseFeatures)
if (typeof featuresFile === 'string' && exists(featuresFile)) {
featuresFiles.push(featuresFile);
featuresFiles.push(featuresFile)
}

@@ -48,3 +48,3 @@

// look for a "named" development file
const files = fs.readdirSync(path.join(process.cwd(), './features'));
const files = fs.readdirSync(path.join(process.cwd(), './features'))
for (let i = 0; i < files.length; i++) {

@@ -55,3 +55,3 @@ if (

) {
featuresFiles.push('./features/' + files[i]);
featuresFiles.push('./features/' + files[i])
}

@@ -64,18 +64,18 @@ }

.map(function(file) {
return require(file);
});
return require(file)
})
const feats = requiredFeatures.reduce((prev, current) => {
return { ...prev, ...current };
}, {});
return { ...prev, ...current }
}, {})
const featureProxy = new Proxy(feats, {
get: (obj, prop) => {
if (obj[prop] === undefined) {
if (typeof prop === 'symbol') return false;
debug(`${prop} feature is undefined`);
return false;
if (typeof prop === 'symbol') return false
debug(`${prop} feature is undefined`)
return false
}
if (obj[prop] === true || obj[prop] === false) {
return obj[prop];
return obj[prop]
}

@@ -87,8 +87,8 @@ if (obj[prop].range !== undefined) {

0
);
)
if (total !== 100) {
throw new Error('Range values must total 100');
throw new Error('Range values must total 100')
}
const range = obj[prop].range.map(v => v / 100);
return random(range) === 0;
const range = obj[prop].range.map(v => v / 100)
return random(range) === 0
}

@@ -103,8 +103,8 @@

const ${obj[prop].parameters} = '${args[1]}'
return ${obj[prop].check}`;
return ${obj[prop].check}`
const funced = new Function(check);
const result = funced();
const funced = new Function(check)
const result = funced()
return result;
return result
}

@@ -114,18 +114,18 @@ const check = `

const { ${obj[prop].parameters} } = ${JSON.stringify(args[1])}
return ${obj[prop].check}`;
const funced = new Function(check);
const result = funced();
return ${obj[prop].check}`
const funced = new Function(check)
const result = funced()
return result;
return result
},
featureProxy,
[prop, ...args]
);
)
},
set: () => {
throw new Error('Not allowed to set a value');
throw new Error('Not allowed to set a value')
}
});
})
return featureProxy;
};
return featureProxy
}
{
"name": "creature-features",
"version": "3.1.0",
"version": "3.1.2",
"description": "Feature flags for node.js",

@@ -11,4 +11,6 @@ "main": "index.js",

"lint": "eslint ./index.js ./test/*.test.js",
"test": "cross-env DEBUG=* jest test/*.test.js",
"cov": "jest test/*.test.js --coverage"
"test": "cross-env jest test/*.test.js",
"cov": "jest test/*.test.js --coverage",
"predocs": " jest test/*.test.js --coverage --coverageDirectory=dist/coverage ",
"docs": "publisher"
},

@@ -33,4 +35,5 @@ "repository": {

},
"homepage": "https://github.com/Kevnz/creature-features#readme",
"homepage": "https://kevinisom.info/creature-features/",
"devDependencies": {
"@kev_nz/publisher": "^1.0.1",
"cross-env": "^5.2.0",

@@ -50,3 +53,10 @@ "eslint": "^5.6.0",

"weighted": "^0.3.0"
},
"jest": {
"coverageReporters": [
"json",
"text",
"html"
]
}
}
}
# Creature Features
## Dead simple feature flags for node.js
[![npm version](https://badge.fury.io/js/fuxor.svg)](https://badge.fury.io/js/fuxor) [![Build Status](https://travis-ci.org/Kevnz/creature-features.svg?branch=master)](https://travis-ci.org/Kevnz/creature-features) [![Coverage](https://img.shields.io/badge/endpoint.svg?color=brightgreen&label=Coverage&logoColor=brightgreen&url=https%3A%2F%2Fkevinisom.info%2Fcreature-features%2Fbadge.json)](https://kevinisom.info/creature-features/coverage/)
## Dead simple feature flags
Creature-Features allows feature flags based on json configuration files and supports true/false, custom rules, and weighted checks.
### Install
```bash
npm install creature-features --save
```
### Configure
#### directory and files
```
- features

@@ -19,4 +30,7 @@ -- default.json (default settings)

```
#### Example File
```json
{

@@ -30,3 +44,5 @@ "FeatureOne": true,

### Usage
```javascript
const features = require('creature-features')();

@@ -42,2 +58,3 @@ // Default behavior

```json
{

@@ -56,3 +73,5 @@ "FeatureOne": true,

```
```javascript
const features = require('creature-features')();

@@ -73,3 +92,5 @@ // Default behavior

#### Weight Based
```json
{

@@ -88,2 +109,3 @@ "FeatureOne": true,

```javascript
const features = require('creature-features')();

@@ -98,6 +120,9 @@ // Roughly 50% of the the time this will be true

}
```
### In Webpack
```javascript
const features = require('creature-features')();

@@ -115,9 +140,13 @@ const webpack = require('webpack');

// in a ui file
if (FEATURES.FeatureOne)
if (FEATURES.FeatureOne) { }
```
### Why?
* [They are useful](http://code.flickr.net/2009/12/02/flipping-out/)
* [What are they?](https://martinfowler.com/articles/feature-toggles.html)
* [How are they useful](http://code.flickr.net/2009/12/02/flipping-out/)
* [More info](http://featureflags.io/)
### Advanced Usage
```javascript

@@ -124,0 +153,0 @@ const creature = require('creature-features');

describe('The Default Behavior of the Module', () => {
beforeAll(() => {
process.env.NODE_ENV = null;
delete process.env.NODE_ENV;
});
process.env.NODE_ENV = null
delete process.env.NODE_ENV
})
it('Load default features', () => {
const creature = require('../index');
const features = creature();
expect(features.FirstFeature).toBe(true);
expect(features.DefaultOverride).toBe(false);
expect(features.IsTest).toBe(false);
});
const creature = require('../index')
const features = creature()
expect(features.FirstFeature).toBe(true)
expect(features.DefaultOverride).toBe(false)
expect(features.IsTest).toBe(false)
})
it('should load named development features', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
const namedFeatures = creature('development');
expect(namedFeatures.IsNamed).toBe(true);
});
process.env.NODE_ENV = 'development'
const creature = require('../index')
const namedFeatures = creature('development')
expect(namedFeatures.IsNamed).toBe(true)
})
it('should load features with a custom environment passed', () => {
process.env.NODE_ENV = 'test';
const creature = require('../index');
const envFeatures = creature('development');
expect(envFeatures.FirstFeature).toBe(true);
expect(envFeatures.IsDevelopment).toBe(true);
expect(envFeatures.IsTest).toBe(false);
});
process.env.NODE_ENV = 'test'
const creature = require('../index')
const envFeatures = creature('development')
expect(envFeatures.FirstFeature).toBe(true)
expect(envFeatures.IsDevelopment).toBe(true)
expect(envFeatures.IsTest).toBe(false)
})
it('should load features with a custom env that does not have a feature file', () => {
process.env.NODE_ENV = 'stable';
const creature = require('../index');
const envFeatures = creature();
process.env.NODE_ENV = 'stable'
const creature = require('../index')
const envFeatures = creature()
expect(envFeatures.FirstFeature).toBe(true);
expect(envFeatures.DefaultOverride).toBe(false);
});
expect(envFeatures.FirstFeature).toBe(true)
expect(envFeatures.DefaultOverride).toBe(false)
})
it('should load features from a custom location and the NODE_ENV has been set', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
process.env.NODE_ENV = 'development'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/'
});
expect(locationFeatures.Location).toBe(true);
expect(locationFeatures.LocationIsTest).toBe(true);
expect(locationFeatures.DefaultLocation).toBe(false);
});
})
expect(locationFeatures.Location).toBe(true)
expect(locationFeatures.LocationIsTest).toBe(true)
expect(locationFeatures.DefaultLocation).toBe(false)
})
it('should load features from a custom location no slash and the NODE_ENV has been set', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
process.env.NODE_ENV = 'development'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features'
});
expect(locationFeatures.Location).toBe(true);
expect(locationFeatures.LocationIsTest).toBe(true);
expect(locationFeatures.DefaultLocation).toBe(false);
});
})
expect(locationFeatures.Location).toBe(true)
expect(locationFeatures.LocationIsTest).toBe(true)
expect(locationFeatures.DefaultLocation).toBe(false)
})
it('should when given a symbol return false and not error', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
process.env.NODE_ENV = 'development'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features'
});
expect(locationFeatures[Symbol('tester')]).toBe(false);
});
})
expect(locationFeatures[Symbol('tester')]).toBe(false)
})
it('should load features from a custom location and a custom environment config object', () => {
process.env.NODE_ENV = 'test';
const creature = require('../index');
process.env.NODE_ENV = 'test'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/',
env: 'development'
});
})
expect(locationFeatures.Location).toBe(true);
expect(locationFeatures.DevelopmentFeature).toBe(true);
});
expect(locationFeatures.Location).toBe(true)
expect(locationFeatures.DevelopmentFeature).toBe(true)
})
it('should load features from a config object with custom location without trailing slash', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
process.env.NODE_ENV = 'development'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features'
});
expect(locationFeatures.Location).toBe(true);
expect(locationFeatures.LocationIsTest).toBe(true);
expect(locationFeatures.DefaultLocation).toBe(false);
});
})
expect(locationFeatures.Location).toBe(true)
expect(locationFeatures.LocationIsTest).toBe(true)
expect(locationFeatures.DefaultLocation).toBe(false)
})
it('should load features from a custom location with a trailing slash', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
process.env.NODE_ENV = 'development'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/'
});
expect(locationFeatures.Location).toBe(true);
expect(locationFeatures.LocationIsTest).toBe(true);
expect(locationFeatures.DefaultLocation).toBe(false);
});
})
expect(locationFeatures.Location).toBe(true)
expect(locationFeatures.LocationIsTest).toBe(true)
expect(locationFeatures.DefaultLocation).toBe(false)
})
it('should throw an error when trying to set a feature', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
const locationFeatures = creature();
process.env.NODE_ENV = 'development'
const creature = require('../index')
const locationFeatures = creature()
expect(() => {
locationFeatures.FirstFeature = true;
}).toThrow();
});
});
locationFeatures.FirstFeature = true
}).toThrow()
})
})
describe('The Default Behavior of the Module', () => {
beforeAll(() => {
process.env.NODE_ENV = null;
delete process.env.NODE_ENV;
});
process.env.NODE_ENV = null
delete process.env.NODE_ENV
})
it('should load named the development features when the NODE_ENV is set to "development"', () => {
process.env.NODE_ENV = 'development';
const creature = require('../index');
const namedFeatures = creature();
expect(namedFeatures.IsNamed).toBe(true);
expect(namedFeatures.IsDevelopmentFromDefault).toBe(true);
expect(namedFeatures.IsDevelopment).toBe(true);
});
process.env.NODE_ENV = 'development'
const creature = require('../index')
const namedFeatures = creature()
expect(namedFeatures.IsNamed).toBe(true)
expect(namedFeatures.IsDevelopmentFromDefault).toBe(true)
expect(namedFeatures.IsDevelopment).toBe(true)
})
it('should load features with a custom env when a different NODE_ENV is already set', () => {
process.env.NODE_ENV = 'test';
const creature = require('../index');
const envFeatures = creature('development');
expect(envFeatures.FirstFeature).toBe(true);
expect(envFeatures.IsDevelopment).toBe(true);
expect(envFeatures.IsDevelopmentFromDefault).toBe(true);
expect(envFeatures.IsTest).toBe(false);
});
process.env.NODE_ENV = 'test'
const creature = require('../index')
const envFeatures = creature('development')
expect(envFeatures.FirstFeature).toBe(true)
expect(envFeatures.IsDevelopment).toBe(true)
expect(envFeatures.IsDevelopmentFromDefault).toBe(true)
expect(envFeatures.IsTest).toBe(false)
})
it('should load features with a production env that does have a feature file', () => {
process.env.NODE_ENV = 'production';
const creature = require('../index');
const envFeatures = creature();
process.env.NODE_ENV = 'production'
const creature = require('../index')
const envFeatures = creature()
expect(envFeatures.IsTest).toBe(false);
expect(envFeatures.IsProduction).toBe(true);
expect(envFeatures.IsCustomLocation).toBe(false);
});
expect(envFeatures.IsTest).toBe(false)
expect(envFeatures.IsProduction).toBe(true)
expect(envFeatures.IsCustomLocation).toBe(false)
})
it('should load features from a custom location and a custom environment based on NODE_ENV setting', () => {
process.env.NODE_ENV = 'production';
const creature = require('../index');
process.env.NODE_ENV = 'production'
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/'
});
})
expect(locationFeatures.Location).toBe(true);
expect(locationFeatures.IsTest).toBe(false);
expect(locationFeatures.IsCustomLocation).toBe(true);
});
});
expect(locationFeatures.Location).toBe(true)
expect(locationFeatures.IsTest).toBe(false)
expect(locationFeatures.IsCustomLocation).toBe(true)
})
})
describe('Feature based on rules', () => {
it('should when passed a parameter evaluate the rule and return true or false', () => {
const creature = require('../index');
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/rules'
});
})
expect(locationFeatures.EmailBasedFeature('test@example.com')).toBe(true);
expect(locationFeatures.EmailBasedFeature('nope@example.com')).toBe(false);
});
expect(locationFeatures.EmailBasedFeature('test@example.com')).toBe(true)
expect(locationFeatures.EmailBasedFeature('nope@example.com')).toBe(false)
})
it('should when passed an object evaluate the rule and return true or false', () => {
const creature = require('../index');
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/rules'
});
})

@@ -23,3 +23,3 @@ expect(

})
).toBe(true);
).toBe(true)
expect(

@@ -30,10 +30,10 @@ locationFeatures.ObjectPassedFeature({

})
).toBe(false);
});
).toBe(false)
})
it('should when passed an object evaluate the rule and return false if missing parameters', () => {
const creature = require('../index');
const creature = require('../index')
const locationFeatures = creature({
location: './test/features/rules'
});
})

@@ -45,3 +45,3 @@ expect(

})
).toBe(true);
).toBe(true)

@@ -53,4 +53,4 @@ expect(

})
).toBe(false);
});
});
).toBe(false)
})
})
describe('Feature based on percents', () => {
it('should take an object with a range of 50 50 and return a % of true results', () => {
const creature = require('../index');
const creature = require('../index')
const features = creature({
location: './test/features/rules'
});
let trueTotal = 0;
})
let trueTotal = 0
for (let index = 0; index < 1000; index++) {
if (features.PercentBasedFiftyFifty) {
trueTotal++;
trueTotal++
}
}
expect(trueTotal > 450).toBe(true);
expect(trueTotal < 550).toBe(true);
});
expect(trueTotal > 450).toBe(true)
expect(trueTotal < 550).toBe(true)
})
it('should take an object with a range of 33 67 and return a % of true results', () => {
const creature = require('../index');
const creature = require('../index')
const features = creature({
location: './test/features/rules'
});
let trueTotal = 0;
})
let trueTotal = 0
for (let index = 0; index < 1000; index++) {
if (features.PercentBasedRuleOneThird) {
trueTotal++;
trueTotal++
}
}
expect(trueTotal > 300).toBe(true);
expect(trueTotal < 375).toBe(true);
});
expect(trueTotal > 300).toBe(true)
expect(trueTotal < 375).toBe(true)
})
it('should when given a range that totals above or below 100 throw an error', () => {
const creature = require('../index');
const creature = require('../index')
const features = creature({
location: './test/features/rules'
});
})
expect(() => features.InvalidUnderPercentRule).toThrow();
expect(() => features.InvalidOverPercentRule).toThrow();
});
expect(() => features.InvalidUnderPercentRule).toThrow()
expect(() => features.InvalidOverPercentRule).toThrow()
})
it('should take an object with a range of 33 67 and return a % of true results for 10000 calls', () => {
const creature = require('../index');
const creature = require('../index')
const features = creature({
location: './test/features/rules'
});
let trueTotal = 0;
})
let trueTotal = 0
for (let index = 0; index < 10000; index++) {
if (features.PercentBasedRuleOneThird) {
trueTotal++;
trueTotal++
}
}
expect(trueTotal > 3000).toBe(true);
expect(trueTotal < 3750).toBe(true);
});
});
expect(trueTotal > 3000).toBe(true)
expect(trueTotal < 3750).toBe(true)
})
})
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc