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

cerebro

Package Overview
Dependencies
Maintainers
2
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cerebro

A generic configuration system for Node.js.

  • 1.3.1
  • latest
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
32
decreased by-56.16%
Maintainers
2
Weekly downloads
 
Created
Source

Cerebro

Build Status Coverage Status

A generic configuration system for Node.js.

Cerebro receives two inputs:

  • the context, which is all data associated with a specific user or request. each individual piece of context is referred to as a dimension.
  • the configuration, which are the values which will be chosen based on what the context is.

Cerebro outputs the resolved configuration.

Usage

import Cerebro from 'cerebro';

const configuration = [{
  setting: 'timer',
  value: 30,
  except: [{
    value: 15,
    environment: ['alpha']
  }]
}];

const context = {
  environment: 'alpha'
};

// initialize Cerebro
const cerebro = new Cerebro(configuration);
const resolvedConfiguration = cerebro.resolveConfig(context);

console.log(resolvedConfiguration);
// -> { timer: 15 } because the setting `timer` is set to 15 when the environment is alpha.
// for all other environments, the resolvedConfiguration would be { timer: 30 }

Configuration Rules

For configuration, Cerebro expects an array that contains objects that have a setting and a value. setting is the name of the configuration setting, and value is the value that will be assigned to it. value can be any JSON data type.

Here is an example configuration object:

[{
  setting: 'timer',
  value: 30,
  except: [{
    value: 15,
    environment: ['alpha']
  }]
}]

Settings must explicitly set the value in the except blocks.

Settings are expected to follow these rules:

  • Setting must be a string.
  • Value must not be undefined.
  • Except must be an array.
  • The elements of except must be objects.
  • An element of except must contain value, and this value must not be undefined.

Settings are defined formally in src/validators/schema.json.

Evaluation Order

If all the criteria in an except block is met, the value in the except block will be used. The except blocks are checked in order, and it exits once it finds a match. If no except block matches fully, then the default value is used.

Here is an example:

import Cerebro from ('cerebro');

const configuration = [{
  setting: 'timer',
  value: 30,
  except: [{
    value: 15,
    environment: ['alpha']
  }, {
    value: 20,
    environment: ['alpha'],
    bucket: 'a'
  }]
}];

const context = {
  environment: 'alpha',
  bucket: 'a'
};

// initialize Cerebro
const cerebro = new Cerebro(configuration);
const resolvedConfiguration = cerebro.resolveConfig(context);

console.log(resolvedConfiguration);
// -> { timer: 15 } because the setting `timer` is set to 15 when the environment is alpha.
// in this case, {timer: 20} would never be hit because {timer: 15} will cause the evaluation
// to end immediately when environment is alpha

Supported Formats for Context Checks in Except

Enums

Given an except block that accepts multiple values for the same dimension, enable the setting if a dimension in the context object matches one of those values. For example, if the configuration has something like this:

[{
  setting: 'enableNewFeature',
  value: false,
  except: [{
    value: true,
    bucket: ['a', 'b']
  }]
}];

The setting should be enabled if the context object contains the dimension bucket: 'a' or bucket: 'b'.

Enums also support two additional options, none and all. Here's what the configuration would look like:

[{
    setting: 'enableNewFeature',
    value: false,
    except: [{
        value: true,
        partner: [
            'all'
        ]
    }]
}];

In this case, if the context object contains any partner, the setting will be enabled. If the context contains no partner, the setting will be disabled. In other words, the setting is only enabled for partners.

[{
  setting: 'enableNewFeature',
  value: false,
  except: [{
    value: true,
    partner: ['none']
  }]
}];

Similar logic applies to none. If the context contains no partner, the setting will be enabled. If the context contains any partner, the setting will be disabled. In other words, the setting is disabled only for partners.

Ranges

A configuration may contain a range of values. The range may be inclusive or exclusive.

An inclusive range looks like this: rangeExample: ['1000..2000']. An exclusive range looks like this: rangeExample: ['1000...2000'].

Here’s an example of a setting using a range:

[{
  setting: 'enableNewFeature',
  value: false,
  except: [{
    value: true,
    userBirthdayYear: ['2000...2010']
  }]
}];

Thus, if the context contains a dimension called userBirthdayYear that is anywhere between 2000 and 2010, exclusive, the setting will be enabled.

Setting Dependency

One setting may depend on another setting. In this case, the configuration would look something like this:

[{
  setting: 'independent',
  value: false,
  except: [{
    value: true,
    environment: ['alpha']
  }]
}, {
  setting: 'dependent',
  value: false,
  except: [{
    value: true,
    setting: 'independent'
  }]
}];

In this case, the dependent setting will not be enabled unless the independent setting is also enabled.

Custom Evaluators

We may want to evaluate a context in a custom way. For example, we may want to use do a partial string match using a regex.

Below is an example of the configuration:

[{
    setting: 'enableNewFeature',
    value: false,
    except: [{
        value: true,
        partialLocale: ['en']
    }]
}]

To use a custom evaluator, you must pass an object called customEvaluators through the options object in the Cerebro constructor. This object will have the name of the dimension it is to evaluate as a key and the evaluation function as value.

The evaluation function will receive two parameters, a dimensionValue, which is the value given in the configuration (en in the above example), and testValue, which is the value passed in the context.

The evaluation function must return a boolean. If it does not return a boolean, the return value will be coerced to a boolean.

For the above configuration, the customEvaluators would look like this:

var customEvaluators = { customDimension: function(dimensionValue, testValue) {} };

The dimensionValue would be [“en”], and the testValue would be whatever the value of customDimension was in the context.

Overrides

We may want to override a certain setting, regardless of what the configuration says. To do this, the client must pass an object, overrides, into the Cerebro constructor. The keys of the overrides object will be the setting names and the value will be the value of the setting. Typically, these overrides would come from the request URL, but any source can be used.

Labels

Label metadata may be added to each entry in the form of an array of strings. This does not affect the evaluation of setting entries into their resulting values. It's stricly meant as a way to contextualize those values.

For instance, values that are not wanted as part of a client payload can be marked "server".

Note that this could be used to create subsets of values that no longer have all of the original entries with contraints such as dependencies.

An alternative way to organize and contextualize is to group settings into separate files.

[{
    setting: 'enableNewFeature',
    labels: ['server', 'namedFeatureGroup'],
    value: false,
    except: [{
        value: true,
        partialLocale: ['en']
    }]
}]

Configuration polling

This option is useful for having live configuration that updates without having to re-deploy your application. A poller object continuously calls a fetch function to retrieve a newer version of the configuration. This is very open ended to allow for different security mechanisms such as mTLS or signed responses.

Optionally (but recommended), a configuration schema can be given to the poller to ensure that the response is a valid configuration.

const Cerebro = require('cerebro');

const poller = new Cerebro.ConfigPoller({
  clientSchema: /* optional schema */,
  interval: 5000,
  fetch: async function() {
    const response = await fetch(/* ... */);

    return response.json();
  }
});

const cerebro = new Cerebro(config, {
  poller
});

Thanks

  • Many thanks to Alasdair Mercer for donating the naming rights :)

FAQs

Package last updated on 19 Dec 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

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