npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rule-based-dynamic-json

v1.0.0

Published

CSV rule-driven dynamic JSON generator and validator for Node.js

Downloads

169

Readme

rule-based-dynamic-json

A headless Node.js 18+ module that uses parsed CSV rule rows to generate a new, rule-only JSON document and validate the generated result.

The engine does not mutate the input, does not preserve unknown fields, and does not use eval() or new Function().

Install

npm install rule-based-dynamic-json

Public API

import { UniversalRuleEngine } from "rule-based-dynamic-json";

const engine = new UniversalRuleEngine(csvRuleObjects);
const result = engine.evaluate(inputData);

evaluate() returns exactly:

{
  isValid: boolean,
  errors: [{ path: string, message: string }],
  nextRequiredFields: [string],
  processedData: object
}

Only data described by active rules is copied into processedData. Invalid rule-addressed values remain in processedData so callers can display and correct them, but an invalid result must not be persisted.

CSV rule columns

| Column | Meaning | | --- | --- | | keyname | Unique business-field identifier | | path | Output path using dot notation and optional [*] wildcards | | type | string, number, boolean, array, or object | | condition | Safe activation expression | | defaultvalue | Typed fallback used when the active input value is missing | | allowedvalue | Comma-separated primitive values | | isrequired | Boolean or the string true / false | | regex | Safe regular-expression source for string values |

The constructor accepts rows already parsed from CSV. CSV parsing is intentionally kept outside the engine so applications can choose their preferred parser, streaming strategy, delimiter, encoding, and storage mechanism.

Complete nested example

Equivalent CSV:

keyname,path,type,condition,defaultvalue,allowedvalue,isrequired,regex
projectName,project.name,string,,,,true,^[A-Z][A-Za-z ]+$
category,category,string,,Residential,"Residential, Commercial",true,
blockName,blocks[*].name,string,,,,true,
unitType,blocks[*].units[*].type,string,,,"Apartment, Penthouse, Studio",true,
unitArea,blocks[*].units[*].areaSqft,number,,,,true,
furnished,blocks[*].units[*].furnished,boolean,,false,,false,
status,blocks[*].units[*].status,string,,Available,"Available, Reserved",true,
terrace,blocks[*].units[*].terraceSqft,number,"type === 'Penthouse' && category === 'Residential'",,,true,

Rules after CSV parsing:

const rules = [
  {
    keyname: "projectName",
    path: "project.name",
    type: "string",
    isrequired: true,
    regex: "^[A-Z][A-Za-z ]+$"
  },
  {
    keyname: "category",
    path: "category",
    type: "string",
    defaultvalue: "Residential",
    allowedvalue: "Residential, Commercial",
    isrequired: true
  },
  {
    keyname: "blockName",
    path: "blocks[*].name",
    type: "string",
    isrequired: true
  },
  {
    keyname: "unitType",
    path: "blocks[*].units[*].type",
    type: "string",
    allowedvalue: "Apartment, Penthouse, Studio",
    isrequired: true
  },
  {
    keyname: "unitArea",
    path: "blocks[*].units[*].areaSqft",
    type: "number",
    isrequired: true
  },
  {
    keyname: "furnished",
    path: "blocks[*].units[*].furnished",
    type: "boolean",
    defaultvalue: "false"
  },
  {
    keyname: "status",
    path: "blocks[*].units[*].status",
    type: "string",
    defaultvalue: "Available",
    allowedvalue: "Available, Reserved",
    isrequired: true
  },
  {
    keyname: "terrace",
    path: "blocks[*].units[*].terraceSqft",
    type: "number",
    condition: "type === 'Penthouse' && category === 'Residential'",
    isrequired: true
  }
];

const input = {
  project: { name: "Sky Gardens", internalCode: "discarded" },
  category: "Residential",
  blocks: [
    {
      name: "Block A",
      units: [
        {
          type: "Penthouse",
          areaSqft: "1450",
          furnished: "true",
          terraceSqft: "250"
        },
        {
          type: "Studio",
          areaSqft: "450",
          terraceSqft: 90
        }
      ]
    }
  ],
  unknownField: "discarded"
};

const engine = new UniversalRuleEngine(rules);
const result = engine.evaluate(input);

The generated processedData is:

{
  "category": "Residential",
  "project": {
    "name": "Sky Gardens"
  },
  "blocks": [
    {
      "name": "Block A",
      "units": [
        {
          "areaSqft": 1450,
          "furnished": true,
          "status": "Available",
          "terraceSqft": 250,
          "type": "Penthouse"
        },
        {
          "areaSqft": 450,
          "furnished": false,
          "status": "Available",
          "type": "Studio"
        }
      ]
    }
  ]
}

The Studio unit's obsolete terraceSqft is removed, numeric and boolean strings are coerced, defaults are generated, and unknown fields are discarded.

Conditions

The embedded expression parser supports:

  • String, number, boolean, null, and undefined literals
  • Nested identifiers such as project.category
  • Parentheses and unary !
  • ===, !==, ==, !=, >, >=, <, and <=
  • && and || with short-circuit evaluation

Function calls, assignments, computed property access, object/array literals, and prototype-related identifiers are rejected during construction.

For a path such as blocks[0].units[1].terraceSqft, identifier lookup uses:

  1. Generated values in blocks[0].units[1]
  2. Input values in blocks[0].units[1]
  3. Generated top-level values
  4. Input top-level values

Generation and validation rules

  • Arrays are input-driven. [*] never invents new elements.
  • Multiple wildcards are recursively expanded to concrete indexed paths.
  • Empty source arrays are preserved as empty generated arrays.
  • undefined, null, and empty strings are treated as missing.
  • Numeric strings are converted only when they represent finite numbers.
  • Boolean strings are converted only when they equal true or false, case-insensitively.
  • Array and object defaults may be provided as JSON strings.
  • Input arrays and objects must already have their declared structural type.
  • Conditions and defaults are evaluated in stabilization rounds, so CSV row order does not control the result.
  • Cyclic/non-converging conditions produce an invalid deterministic result.
  • Regex patterns are screened with safe-regex2; regex input is capped at 10,000 characters.

An array or object rule authorizes the full value at that exact path. Applications requiring field-level projection inside a structure should define rules for its child paths instead of defining a rule for the whole parent.

Run tests

npm test

The test suite covers nested wildcard generation, contextual conditions, coercion, defaults, pruning, required-field discovery, enum and regex errors, input immutability, malicious expressions and paths, unsafe regexes, duplicate rules, and non-converging conditions.