{"id":30956067,"url":"https://github.com/crafts69guy/rule-engine-js","last_synced_at":"2025-09-11T12:07:07.411Z","repository":{"id":308855389,"uuid":"1034228055","full_name":"crafts69guy/rule-engine-js","owner":"crafts69guy","description":"Flexible JavaScript rule engine for dynamic business logic, validation, and decision-making with JSON-based rules and built-in security.","archived":false,"fork":false,"pushed_at":"2025-08-24T12:12:05.000Z","size":666,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"production","last_synced_at":"2025-08-24T17:56:17.645Z","etag":null,"topics":["conditional-logic","dynamic-rules","expression-engine","javascript","json-rules","nodejs","rule-engine","rules-as-data","typescript","validation"],"latest_commit_sha":null,"homepage":"https://crafts69guy.github.io/rule-engine-js/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/crafts69guy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-08T04:05:37.000Z","updated_at":"2025-08-24T12:12:08.000Z","dependencies_parsed_at":"2025-08-24T14:08:54.505Z","dependency_job_id":"b0d44d4a-84fe-45c1-9037-164c5571cdc6","html_url":"https://github.com/crafts69guy/rule-engine-js","commit_stats":null,"previous_names":["crafts69guy/rule-engine-js"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/crafts69guy/rule-engine-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crafts69guy%2Frule-engine-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crafts69guy%2Frule-engine-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crafts69guy%2Frule-engine-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crafts69guy%2Frule-engine-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crafts69guy","download_url":"https://codeload.github.com/crafts69guy/rule-engine-js/tar.gz/refs/heads/production","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crafts69guy%2Frule-engine-js/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274632627,"owners_count":25321251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-11T02:00:13.660Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["conditional-logic","dynamic-rules","expression-engine","javascript","json-rules","nodejs","rule-engine","rules-as-data","typescript","validation"],"created_at":"2025-09-11T12:02:53.360Z","updated_at":"2025-09-11T12:07:07.400Z","avatar_url":"https://github.com/crafts69guy.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Rule Engine JS\n\n[![npm version](https://img.shields.io/npm/v/rule-engine-js.svg)](https://www.npmjs.com/package/rule-engine-js)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Build Status](https://img.shields.io/github/workflow/status/crafts69guy/rule-engine-js/CI)](https://github.com/crafts69guy/rule-engine-js/actions)\n[![Coverage Status](https://img.shields.io/codecov/c/github/crafts69guy/rule-engine-js.svg)](https://codecov.io/gh/crafts69guy/rule-engine-js)\n\n\u003e A powerful, flexible JavaScript rule engine for dynamic business logic evaluation. Build complex conditional logic with simple, readable syntax.\n\n## 🚀 Why Rule Engine JS?\n\n**Stop hardcoding business logic.** Rule Engine JS lets you define complex conditional logic as data, making your applications more flexible, maintainable, and business-friendly.\n\n```javascript\n// Instead of this hardcoded logic...\nif (user.age \u003e= 18 \u0026\u0026 user.role === 'admin' \u0026\u0026 user.permissions.includes('write')) {\n  return true;\n}\n\n// Write this declarative rule...\nconst rule = rules.and(\n  rules.gte('age', 18),\n  rules.eq('role', 'admin'),\n  rules.in('write', 'permissions')\n);\n\nconst result = engine.evaluateExpr(rule, user);\n```\n\n## ✨ Key Features\n\n- **🎯 Zero Dependencies** - Lightweight with no external dependencies\n- **⚡ High Performance** - Intelligent caching with LRU eviction\n- **🔒 Security First** - Built-in protection against prototype pollution\n- **🧩 Dynamic Field Comparison** - Compare values across different data paths\n- **📝 Type Safe** - Full TypeScript support with comprehensive type definitions\n- **🔧 Extensible** - Easy custom operator registration\n- **📊 Monitoring** - Built-in performance metrics and cache statistics\n\n## 📦 Installation\n\n```bash\nnpm install rule-engine-js\n```\n\n```bash\nyarn add rule-engine-js\n```\n\n## 🎯 Quick Start\n\n```javascript\nimport { createRuleEngine, createRuleHelpers } from 'rule-engine-js';\n\n// Create engine and helpers\nconst engine = createRuleEngine();\nconst rules = createRuleHelpers();\n\n// Your data\nconst user = {\n  name: 'John Doe',\n  age: 28,\n  role: 'admin',\n  email: 'john@company.com',\n  permissions: ['read', 'write', 'delete'],\n};\n\n// Simple rule\nconst isAdult = rules.gte('age', 18);\nconsole.log(engine.evaluateExpr(isAdult, user).success); // true\n\n// Complex rule\nconst canAccess = rules.and(\n  rules.gte('age', 18),\n  rules.eq('role', 'admin'),\n  rules.validation.email('email'),\n  rules.in('write', 'permissions')\n);\n\nconsole.log(engine.evaluateExpr(canAccess, user).success); // true\n```\n\n## 🏗️ Core Concepts\n\n### Rules are Data\n\nRules are simple JSON objects that describe conditions:\n\n```javascript\n// This rule...\nconst rule = { and: [{ gte: ['age', 18] }, { eq: ['role', 'admin'] }] };\n\n// Is equivalent to this helper syntax...\nconst rule = rules.and(rules.gte('age', 18), rules.eq('role', 'admin'));\n```\n\n### Dynamic Field Comparison\n\nCompare values from different paths in your data:\n\n```javascript\nconst formData = {\n  password: 'secret123',\n  confirmPassword: 'secret123',\n  score: 85,\n  maxScore: 100,\n};\n\nconst rule = rules.and(\n  rules.field.equals('password', 'confirmPassword'),\n  rules.lt('score', 'maxScore')\n);\n```\n\n### Path Resolution\n\nAccess nested data with dot notation:\n\n```javascript\nconst user = {\n  profile: {\n    settings: {\n      theme: 'dark',\n      notifications: true,\n    },\n  },\n};\n\nconst rule = rules.eq('profile.settings.theme', 'dark');\n```\n\n## 🛠️ Common Use Cases\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e🔐 User Access Control\u003c/strong\u003e\u003c/summary\u003e\n\n```javascript\nconst accessRule = rules.and(\n  // User must be active\n  rules.isTrue('user.isActive'),\n\n  // Either admin OR (department match AND has permission)\n  rules.or(\n    rules.eq('user.role', 'admin'),\n    rules.and(\n      rules.field.equals('user.department', 'resource.department'),\n      rules.in('write', 'user.permissions')\n    )\n  )\n);\n\nconst context = {\n  user: {\n    isActive: true,\n    role: 'editor',\n    department: 'engineering',\n    permissions: ['read', 'write'],\n  },\n  resource: { department: 'engineering' },\n};\n\nconst hasAccess = engine.evaluateExpr(accessRule, context);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e💰 Dynamic Pricing \u0026 Discounts\u003c/strong\u003e\u003c/summary\u003e\n\n```javascript\nconst discountRule = rules.or(\n  // VIP customers with minimum order\n  rules.and(rules.eq('customer.type', 'vip'), rules.gte('order.total', 100)),\n\n  // High loyalty points\n  rules.gte('customer.loyaltyPoints', 1000),\n\n  // Large orders\n  rules.gte('order.total', 200)\n);\n\nconst orderData = {\n  customer: { type: 'vip', loyaltyPoints: 500 },\n  order: { total: 150 },\n};\n\nconst eligible = engine.evaluateExpr(discountRule, orderData);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e📝 Form Validation\u003c/strong\u003e\u003c/summary\u003e\n\n```javascript\nconst validationRule = rules.and(\n  rules.validation.required('firstName'),\n  rules.validation.required('lastName'),\n  rules.validation.email('email'),\n  rules.validation.ageRange('age', 18, 120),\n  rules.validation.minLength('password', 8),\n  rules.validation.maxLength('username', 20),\n  rules.field.equals('password', 'confirmPassword'),\n  rules.isTrue('agreedToTerms')\n);\n\nconst formData = {\n  firstName: 'John',\n  lastName: 'Doe',\n  email: 'john@example.com',\n  age: 25,\n  username: 'johndoe',\n  password: 'secret123',\n  confirmPassword: 'secret123',\n  agreedToTerms: true,\n};\n\nconst isValid = engine.evaluateExpr(validationRule, formData);\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003e🏦 Loan Approval Logic\u003c/strong\u003e\u003c/summary\u003e\n\n```javascript\nconst approvalRule = rules.and(\n  rules.gte('applicant.creditScore', 650),\n  rules.gte('applicant.income', 50000),\n  rules.lte('applicant.debtRatio', 0.4),\n  rules.gte('applicant.employmentYears', 2),\n  rules.between('applicant.age', [18, 70]),\n  rules.in('loan.purpose', ['home', 'car', 'education'])\n);\n\nconst application = {\n  applicant: {\n    creditScore: 720,\n    income: 75000,\n    debtRatio: 0.25,\n    employmentYears: 3,\n    age: 32,\n  },\n  loan: {\n    amount: 250000,\n    purpose: 'home',\n  },\n};\n\nconst approved = engine.evaluateExpr(approvalRule, application);\n```\n\n\u003c/details\u003e\n\n## 📚 Available Operators\n\n| Category       | Operators                                                                                        | Description                               |\n| -------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------- |\n| **Comparison** | `eq`, `neq`, `gt`, `gte`, `lt`, `lte`                                                            | Compare values with type coercion support |\n| **Logical**    | `and`, `or`, `not`                                                                               | Combine multiple conditions               |\n| **String**     | `contains`, `startsWith`, `endsWith`, `regex`                                                    | Text pattern matching                     |\n| **Array**      | `in`, `notIn`                                                                                    | Check membership in arrays                |\n| **Special**    | `between`, `isNull`, `isNotNull`                                                                 | Range and null checking                   |\n| **Validation** | `email`, `required`, `ageRange`, `oneOf`, `minLength`, `maxLength`, `lengthRange`, `exactLength` | Common validation patterns                |\n\n## ⚡ Performance Features\n\n- **LRU Caching**: Expression results and path resolutions are cached\n- **Regex Compilation**: Patterns are compiled once and reused\n- **Metrics Tracking**: Monitor performance with built-in metrics\n- **Bundle Optimization**: Multiple output formats (UMD, ESM, CommonJS)\n\n```javascript\n// Get performance metrics\nconst metrics = engine.getMetrics();\nconsole.log({\n  evaluations: metrics.evaluations,\n  cacheHits: metrics.cacheHits,\n  avgTime: metrics.avgTime,\n});\n\n// Get cache statistics\nconst cacheStats = engine.getCacheStats();\nconsole.log(cacheStats);\n```\n\n## 🔒 Security Features\n\n- **Prototype Pollution Protection**: Automatically blocks dangerous paths\n- **Function Access Prevention**: Functions are blocked by default\n- **Safe Path Resolution**: Only accesses own properties\n- **Configurable Security**: Adjust security settings as needed\n\n```javascript\n// Secure by default\nconst maliciousData = { __proto__: { isAdmin: true } };\nengine.resolvePath(maliciousData, '__proto__.isAdmin'); // Returns undefined\n\n// Configure security\nconst engine = createRuleEngine({\n  allowPrototypeAccess: false, // Always false in production\n  strict: true, // Enable strict type checking\n  maxDepth: 10, // Prevent deep recursion\n  maxOperators: 100, // Limit complexity\n});\n```\n\n## 🎨 Custom Operators\n\nExtend the engine with your own business logic:\n\n```javascript\n// Register business-specific logic\nengine.registerOperator('isBusinessHours', (args, context) =\u003e {\n  const [timezone = 'UTC'] = args;\n  const now = new Date();\n  const hour = now.getUTCHours();\n  return hour \u003e= 9 \u0026\u0026 hour \u003c 17; // 9 AM to 5 PM UTC\n});\n\n// Usage\nconst rule = rules.and(\n  { isBusinessHours: ['America/New_York'] },\n  rules.isTrue('support.available')\n);\n```\n\n## 📖 Documentation\n\n- **[Complete Documentation](./docs/README.md)** - Full API reference and guides\n- **[Quick Start Guide](./docs/quick-start.md)** - Get up and running in minutes\n- **[Operator Reference](./docs/operators.md)** - Complete operator documentation\n- **[Performance Guide](./docs/performance.md)** - Optimization tips and tricks\n- **[Security Guide](./docs/security.md)** - Security best practices\n- **[Examples](./examples/)** - Real-world examples and patterns\n\n## 🚀 Framework Integration\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eExpress.js Middleware\u003c/strong\u003e\u003c/summary\u003e\n\n```javascript\nimport { createRuleEngine, createRuleHelpers } from 'rule-engine-js';\n\nconst engine = createRuleEngine();\nconst rules = createRuleHelpers();\n\nfunction createAccessMiddleware(accessRule) {\n  return (req, res, next) =\u003e {\n    const result = engine.evaluateExpr(accessRule, req.user);\n    if (result.success) {\n      next();\n    } else {\n      res.status(403).json({ error: 'Access denied' });\n    }\n  };\n}\n\n// Usage\nconst adminRule = rules.eq('role', 'admin');\napp.get('/admin/*', createAccessMiddleware(adminRule));\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eReact Form Validation\u003c/strong\u003e\u003c/summary\u003e\n\n```javascript\nimport { createRuleEngine, createRuleHelpers } from 'rule-engine-js';\n\nconst engine = createRuleEngine();\nconst rules = createRuleHelpers();\n\nfunction useFormValidation(validationRules) {\n  const validateForm = (formData) =\u003e {\n    const results = {};\n\n    Object.entries(validationRules).forEach(([field, rule]) =\u003e {\n      const result = engine.evaluateExpr(rule, formData);\n      results[field] = {\n        isValid: result.success,\n        error: result.success ? null : result.error,\n      };\n    });\n\n    return results;\n  };\n\n  return { validateForm };\n}\n\n// Usage\nconst validationRules = {\n  email: rules.validation.email('email'),\n  username: rules.validation.lengthRange('username', 3, 20),\n  password: rules.and(\n    rules.validation.minLength('password', 8),\n    rules.regex('password', '(?=.*[0-9])(?=.*[a-zA-Z])')\n  ),\n};\n```\n\n\u003c/details\u003e\n\n## 🧪 Testing\n\n```javascript\nimport { createRuleEngine, createRuleHelpers } from 'rule-engine-js';\n\ndescribe('User Access Rules', () =\u003e {\n  const engine = createRuleEngine();\n  const rules = createRuleHelpers();\n\n  test('admin has full access', () =\u003e {\n    const user = { role: 'admin' };\n    const rule = rules.eq('role', 'admin');\n\n    const result = engine.evaluateExpr(rule, user);\n    expect(result.success).toBe(true);\n  });\n});\n```\n\n## 📊 Benchmarks\n\n| Operation            | Speed   | Cache Hit Rate |\n| -------------------- | ------- | -------------- |\n| Simple equality      | ~0.1ms  | 95%            |\n| Complex nested rules | ~2ms    | 85%            |\n| Regex operations     | ~0.5ms  | 90%            |\n| Path resolution      | ~0.05ms | 98%            |\n\n_Benchmarks run on Node.js 18, Intel i7, with 1000 rule evaluations_\n\n## 🤝 Contributing\n\nWe welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Write tests for your changes\n4. Ensure all tests pass (`npm test`)\n5. Commit your changes (`git commit -m 'Add amazing feature'`)\n6. Push to the branch (`git push origin feature/amazing-feature`)\n7. Open a Pull Request\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🙏 Acknowledgments\n\n- Inspired by business rule engines and decision tables\n- Built for modern JavaScript applications\n- Designed with security and performance in mind\n\n## 📚 Related Projects\n\n- [JSON Schema](https://json-schema.org/) - For data validation\n- [Joi](https://joi.dev/) - Object schema validation\n- [Yup](https://github.com/jquense/yup) - Schema builder for runtime value parsing\n\n---\n\n**[📖 Read the Full Documentation](./docs/README.md)** | **[🚀 View Examples](./examples/)** | **[💬 Get Support](https://github.com/crafts69guy/rule-engine-js/discussions)**\n\nMade with ❤️ by [Crafts69Guy](https://github.com/crafts69guy)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrafts69guy%2Frule-engine-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrafts69guy%2Frule-engine-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrafts69guy%2Frule-engine-js/lists"}