Confidence Configuration Engine

repository·master·Indexed 42 days ago

https://github.com/hapipal/confidence

A dynamic, declarative configuration engine for Node.js v12+ that serves values from JSON-based files using object paths. It supports dynamic value resolution through special directives including $param for parameter substitution, $coerce for type casting, $default for fallbacks, $filter for criteria-based selection, and $range for numerical buckets.

Tokens
4.7K
Snippets
21
Records
23
Agent score
54%

What's inside Confidence

  1. Basic structure of a Confidence document

    master

    Confidence documents are built on top of plain JavaScript objects.

    • Key Naming: Keys must contain only alphanumeric characters and underscores (_).
    • Reserved Prefix: The $ prefix is reserved for special directives.
    • Values: Values can be any non-object type (strings, numbers, booleans) or arrays. Keys can also contain nested objects.
    {
        "key1": "abc",
        "key2": 2,
        "key3": {
            "sub1": 123
        }
    }
  2. Use environment variables with the $env directive

    master

    You can pull values from environment variables using the $env directive. This is useful for sensitive information like database credentials.

    • Fallback: Use the $default directive to provide a fallback value if the environment variable is not set.
    {
        "mysql": {
            "host": { "$env" : "MYSQL_HOST" },
            "port": { "$env" : "MYSQL_PORT", "$default": 3306 }
        }
    }
  3. Apply filters to configuration keys

    master

    A key can provide multiple values based on a filter key present in the runtime criteria.

    • Filter Name: The $filter key specifies which criteria key to look at. Filter names must be alphanumeric or underscores.
    • Nested Filters: Filters can point to nested criteria using dot-notation (e.g., system.env).
    • Dynamic Filters: The $filter value itself can be a directive like $env.
    • Fallback: Use $default to provide a value if the filter criteria is missing or the value does not match any defined filter option.
    {
        "key2": {
            "$filter": "system.env",
            "production": 1,
            "$default": 2
        }
    }
  4. How Confidence works

    master

    Confidence is a configuration document format and API designed to serve values from JSON-based configurations using object paths (e.g., '/a/b/c' maps to a.b.c).

    It supports dynamic value resolution through special $-prefixed keys that allow for:

    • Parameter substitution: Using $param to inject values from a provided context (like process.env).
    • Type coercion: Using $coerce to cast values to specific types.
    • Default values: Using $default to provide fallback values.
    • Filtering: Using $filter to select specific configuration branches based on criteria (e.g., environment variables).
  5. Share values using the $base directive

    master

    The $base directive allows you to define shared values that are automatically merged into filtered results, reducing duplication.

    Merging Rules:

    • Objects: If a key exists in both $base and the filtered value, the filtered value overrides the $base value.
    • Arrays: Arrays are merged by default. To replace the $base array instead of merging, use the $value directive combined with the $replace: true flag.
    {
      "$filter": "env",
      "$base": {
          "logLocation": "/logs",
          "flags": ["a", "b"],
          "tags": {
              "$value": ["DEBUG"],
              "$replace": true
          }
      },
      "production":  {
          "logLevel": "error",
          "flags": ["c", "d"]
      }
    }
  6. How configuration filtering and resolution works

    master

    The Store uses a recursive mechanism to resolve values based on special keys in the configuration document. When you call get(), the store traverses the tree and applies the following logic:

    Special Configuration Keys

    • $value: Defines the actual value of a node. The store will attempt to resolve this value.
    • $filter: A mechanism to select a specific branch based on a criterion. The criterion can be resolved from the criteria object passed to get() or from process.env if $env is specified.
    • $range: Used with $filter to provide a list of conditional values. The store iterates through the range and selects the first value where the criterion is less than or equal to the limit.
    • $default: A fallback value used if the primary filter or resolution fails.
    • $base: Used in conjunction with $value to merge a base configuration with the resolved value.
    • $env: Tells the store to look up a value in process.env using a path provided by the $env key.
    • $param: Tells the store to look up a value in the criteria object provided during the get() call.
    • $coerce: Specifies the type to which the resolved value should be converted (e.g., 'number', 'boolean', 'array', 'object').
  7. Example of Confidence configuration and resolution

    master

    The following example demonstrates how @hapipal/confidence resolves a configuration object based on provided criteria.

    Input Configuration

    {
        "key1": "abc",
        "key2": {
            "$filter": "env",
            "production": {
                "deeper": {
                    "$value": "value"
                }
            },
            "$default": {
                "$filter": "platform",
                "android": 0,
                "ios": 1,
                "$default": 2
            }
        },
        "key3": {
            "sub1": 123,
            "sub2": {
                "$filter": "xfactor",
                "yes": 6
            }
        },
        "ab": {
            "$filter": "random.a",
            "$range": [
                { "limit": 10, "value": 4 },
                { "limit": 20, "value": 5 }
            ],
            "$default": 6
        },
        "$meta": {
            "description": "example file"
        }
    }

    Scenario 1: No criteria applied

    When no criteria are provided, the configuration resolves to its default values:

    {
        "key1": "abc",
        "key2": 2,
        "key3": {
            "sub1": 123
        },
        "ab": 6
    }

    Scenario 2: With criteria applied

    If the following criteria are applied:

    {
        "env": "production",
        "platform": "ios",
        "xfactor": "yes",
        "random": {
            "a": 15
        }
    }

    The configuration resolves to:

    {
        "key1": "abc",
        "key2": {
            "deeper": "value"
        },
        "key3": {
            "sub1": 123,
            "sub2": 6
        },
        "ab": 5
    }
  8. Use Confidence to resolve dynamic configurations

    master

    To use Confidence, instantiate a Confidence.Store with your configuration object. You can then use the .get(path, context) method to resolve values. The context object (typically process.env) is used to satisfy $param keys.

    Note: Confidence requires Node.js v12+.

    const Confidence = require('@hapipal/confidence');
    
    const store = new Confidence.Store({
        server: {
            host: 'localhost',
            port: {
                $param: 'PORT',
                $coerce: 'number',
                $default: 3000
            },
            debug: {
                $filter: 'NODE_ENV',
                $default: {
                    log: ['error'],
                    request: ['error']
                },
                production: {
                    request: ['implementation']
                }
            }
        }
    });
    
    // Resolve the configuration using process.env as the context
    const config = store.get('/', process.env);
  9. Use numerical ranges with the $range directive

    master

    The $range directive allows you to select a value based on numerical buckets. The value provided in the criteria must be an integer.

    Confidence matches the criteria value against the sorted range entries and selects the entry with the lowest limit that is still greater than or equal to the criteria value. If the criteria value exceeds all limits, it falls back to the $default value.

    {
        "key3": {
            "$filter": "random.a",
            "$range": [
                { "limit": 10, "value": 4 },
                { "limit": 20, "value": 5 }
            ],
            "$default": 6
        }
    }
  10. Load a configuration document into a Store

    master

    Use store.load(document) to populate the store. This method validates the provided configuration, clears any existing configuration in the store, and then loads the new document. The document must be an object containing a valid Confidence configuration. If the document is invalid, an error will be thrown.

    const document = {
        a: 1,
        b: 2,
        c: {
            $filter: 'size',
            big: 100,
            small: 1,
            $default: 50
        }
    };
    
    store.load(document);
  11. Retrieve values with store.get()

    master

    Use store.get(key, [criteria]) to retrieve a value from the configuration.

    • key: A string representing the requested key path. All keys must begin with /. Using / returns the entire document.
    • criteria: An optional object used to apply filters defined in the configuration document. Defaults to {}.

    If the key is invalid or not found, the method returns undefined.

    // Returns the value at path '/c' after applying the 'size: big' criteria
    const value = store.get('/c', { size: 'big' });