@run-iq/core
v0.2.5
Published
PPE — Parametric Policy Engine core
Maintainers
Readme
@run-iq/core
Parametric Policy Engine (PPE) — a deterministic, plugin-driven rules engine for financial and regulatory computation.
Install
npm install @run-iq/coreQuick start
import { PPEEngine } from '@run-iq/core';
const engine = new PPEEngine({
plugins: [fiscalPlugin],
dsls: [jsonLogicEvaluator],
snapshot: snapshotAdapter,
strict: true,
timeout: { dsl: 100, hook: 500, pipeline: 5000 },
});
const result = await engine.evaluate(rules, {
requestId: 'req-001',
data: { grossSalary: 2_500_000 },
meta: { tenantId: 'tenant-1' },
});
console.log(result.value); // aggregated value
console.log(result.breakdown); // per-rule contributions
console.log(result.trace); // full audit trailPipeline
The engine executes a 10-step pipeline on every call to evaluate():
- Input validation — sanitize and validate the input
- Idempotence check — skip if
requestIdalready processed - beforeEvaluate hooks — plugins enrich input and can modify rules via
BeforeEvaluateResult - Rule filtering — date range, tags, DSL conditions
- Rule validation — checksum + params verification
- Dominance resolution — priority sort + conflict handling
- Execution — models compute contributions (decimal-safe)
- afterEvaluate hooks — plugins enrich result
- Snapshot — persist immutable audit record
- Return — final
EvaluationResult
Key concepts
| Concept | Description |
|---|---|
| Rule | Declarative policy unit with model, params, condition, priority, and date range |
| CalculationModel | Pure function (input, rule, params) → number — registered by plugins |
| DSLEvaluator | Evaluates rule conditions (e.g. JSONLogic, CEL) — pluggable |
| PPEPlugin | Lifecycle hooks (beforeEvaluate, afterEvaluate) + model registration |
| BeforeEvaluateResult | Return type from beforeEvaluate containing both input and rules to evaluate |
| Snapshot | Immutable audit record with full rule copies and DSL versions |
Retroactive calculation
Pass effectiveDate to evaluate rules as of a specific date:
const result = await engine.evaluate(rules, {
requestId: 'req-002',
data: { grossSalary: 2_500_000 },
meta: {
tenantId: 'tenant-1',
effectiveDate: new Date('2023-12-01'),
},
});Hydrating rules from JSON
When rules come from an API or database as JSON, date fields are strings. Use hydrateRule to convert them:
import { hydrateRule, hydrateRules } from '@run-iq/core';
const rule = hydrateRule(jsonFromApi);
const rules = hydrateRules(jsonArrayFromDb);Exported errors
| Error | Code | When |
|---|---|---|
| RuleConflictError | RULE_CONFLICT | Same-priority rules in strict mode |
| ModelNotFoundError | MODEL_NOT_FOUND | Rule references unregistered model |
| DSLNotFoundError | DSL_NOT_FOUND | Rule condition uses unregistered DSL |
| DSLTimeoutError | DSL_TIMEOUT | DSL evaluation exceeds timeout |
| DSLEvaluationError | DSL_EVALUATION_ERROR | DSL syntax or runtime error |
| SnapshotFailureError | SNAPSHOT_FAILURE | Snapshot save fails in strict mode |
| ValidationError | VALIDATION_ERROR | Input validation fails |
Requirements
- Node.js >= 20
- TypeScript >= 5.4
License
MIT
