verdict-rules
Version:
A small, zero-dependency, async-native rule-evaluation engine. Compose conditions into one explainable pass/fail verdict, with sequential evaluation and real short-circuiting as a guarantee.
214 lines (209 loc) • 7.81 kB
JavaScript
"use strict";
var VerdictRules = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
// src/index.ts
var index_exports = {};
__export(index_exports, {
AndRule: () => AndRule,
FunctionRule: () => FunctionRule,
OrRule: () => OrRule,
RulesEngine: () => RulesEngine,
UnknownLookupError: () => UnknownLookupError
});
// src/errors.ts
var UnknownLookupError = class extends Error {
constructor(kind, key) {
super(
kind === "rule" ? `No rule named '${key}' in this engine` : `No rules in group '${key}' in this engine`
);
/** Whether the missing thing was a rule name or a group label. */
__publicField(this, "kind");
/** The name or label that matched nothing. */
__publicField(this, "key");
this.name = "UnknownLookupError";
this.kind = kind;
this.key = key;
}
};
// src/engine.ts
var _rules, _byName, _byGroup;
var RulesEngine = class {
constructor(rules) {
__privateAdd(this, _rules);
__privateAdd(this, _byName);
__privateAdd(this, _byGroup);
__privateSet(this, _rules, [...rules]);
__privateSet(this, _byName, new Map(rules.map((r) => [r.name, r])));
__privateSet(this, _byGroup, /* @__PURE__ */ new Map());
for (const rule of rules) {
if (rule.group === void 0 || rule.group === "") continue;
const existing = __privateGet(this, _byGroup).get(rule.group);
if (existing === void 0) __privateGet(this, _byGroup).set(rule.group, [rule]);
else existing.push(rule);
}
}
/** Every rule name registered here, in registration order. */
get ruleNames() {
return [...__privateGet(this, _byName).keys()];
}
/** Every group label carried by at least one rule, in first-seen order. */
get groupNames() {
return [...__privateGet(this, _byGroup).keys()];
}
/** Evaluate every registered rule. Never short-circuits. */
async runAll(context) {
const results = [];
for (const rule of __privateGet(this, _rules)) {
results.push(await rule.evaluate(context));
}
return { passed: results.every((r) => r.passed), results };
}
/**
* Evaluate one rule by name, or return `undefined` if no such rule exists.
*
* `undefined` means absent, never failed.
*/
async tryRunNamed(name, context) {
const rule = __privateGet(this, _byName).get(name);
if (rule === void 0) return void 0;
return rule.evaluate(context);
}
/**
* Evaluate exactly one rule, looked up by name.
*
* @throws {UnknownLookupError} if no rule has this name.
*/
async runNamed(name, context) {
const result = await this.tryRunNamed(name, context);
if (result === void 0) {
throw new UnknownLookupError("rule", name);
}
return result;
}
/**
* Evaluate a group, or return `undefined` if no such group exists.
*
* `undefined` means absent, never vacuously passed.
*/
async tryRunGroup(group, context) {
const rules = __privateGet(this, _byGroup).get(group);
if (rules === void 0 || rules.length === 0) return void 0;
const results = [];
for (const rule of rules) {
results.push(await rule.evaluate(context));
}
return { passed: results.every((r) => r.passed), results };
}
/**
* Evaluate every rule sharing a group label. Never short-circuits.
*
* @throws {UnknownLookupError} if no rule carries this label.
*/
async runGroup(group, context) {
const result = await this.tryRunGroup(group, context);
if (result === void 0) {
throw new UnknownLookupError("group", group);
}
return result;
}
};
_rules = new WeakMap();
_byName = new WeakMap();
_byGroup = new WeakMap();
// src/rule.ts
var _predicate;
var FunctionRule = class {
constructor(name, predicate, group) {
__publicField(this, "name");
__publicField(this, "group");
__privateAdd(this, _predicate);
this.name = name;
this.group = group;
__privateSet(this, _predicate, predicate);
}
/** Runs the wrapped predicate and returns whatever it returns, unchanged. */
evaluate(context) {
return __privateGet(this, _predicate).call(this, context);
}
};
_predicate = new WeakMap();
var _rules2;
var AndRule = class {
constructor(name, rules, group) {
__publicField(this, "name");
__publicField(this, "group");
__privateAdd(this, _rules2);
this.name = name;
this.group = group;
__privateSet(this, _rules2, rules);
}
async evaluate(context) {
const subResults = [];
for (const rule of __privateGet(this, _rules2)) {
const result = await rule.evaluate(context);
subResults.push(result);
if (!result.passed) {
const detail = result.detail ? `'${rule.name}' failed: ${result.detail}` : `'${rule.name}' failed`;
return { ruleName: this.name, passed: false, detail, data: subResults };
}
}
return { ruleName: this.name, passed: true, data: subResults };
}
};
_rules2 = new WeakMap();
var _rules3;
var OrRule = class {
constructor(name, rules, group) {
__publicField(this, "name");
__publicField(this, "group");
__privateAdd(this, _rules3);
this.name = name;
this.group = group;
__privateSet(this, _rules3, rules);
}
async evaluate(context) {
const subResults = [];
for (const rule of __privateGet(this, _rules3)) {
const result = await rule.evaluate(context);
subResults.push(result);
if (result.passed) {
return { ruleName: this.name, passed: true, data: subResults };
}
}
return {
ruleName: this.name,
passed: false,
detail: "no sub-rule passed",
data: subResults
};
}
};
_rules3 = new WeakMap();
return __toCommonJS(index_exports);
})();
//# sourceMappingURL=verdict-rules.global.js.map