Rules
Rules are the core building blocks of the Standard Rule Engine. They define the logic that processes facts and updates context.
Creating Rules
Rules are created using the rule()
method on an engine:
ts
const engine = new Engine().rule("example", (facts, { context }) => {
// Rule logic here
});
Rule Structure
Each rule consists of:
- A unique name
- A handler function
- Optional schema validation
ts
const engine = new Engine().context("processed", false).rule(
"process-user", // Rule name
(facts, { context }) => {
// Handler function
context.processed = true;
},
{
// Optional schema
schema: z.object({
id: z.string(),
name: z.string(),
}),
},
);
Rule Handlers
Rule handlers receive two parameters:
facts
: The data to processoptions
: An object containingcontext
andhelpers
ts
const engine = new Engine()
.context("count", 0)
.helper("increment", (context) => {
context.count++;
})
.rule(
"example",
(facts, { context, helpers }) => {
helpers.increment();
console.log(`Processing ${facts.id}`);
},
{
schema: type({ id: "string" }),
},
);
Priority
Rules can be given a priority to control the order they are executed in. Rules with a lower priority number are executed first. The default priority is 1.
ts
const engine = new Engine()
.rule(
"alwaysRunFirst",
(_, { context }) => {
console.log("im first");
},
{
priority: 0,
},
)
.rule(
"alwaysRunLast",
(_, { context }) => {
console.log("im last");
},
{
priority: 100,
},
);
const session = engine.createSession().insert({}).fire();
// Output:
// im first
// im last
Schema Validation
Rules can specify a schema to validate facts. The schema property can take any schema validation library that implements the Standard Schema.
ts
const engine = new Engine().context({ isValid: false }).rule(
"validate-user",
(user, { context }) => {
context.isValid = true;
},
{
schema: z.object({
id: z.string(),
email: z.string().email(),
age: z.number().min(18),
}),
},
);
Example: User Validation
ts
import { Engine } from "standard-rule-engine";
import { z } from "zod";
const userSchema = z.object({
id: z.string(),
email: z.string().email(),
age: z.number(),
password: z.string().min(8),
});
const engine = new Engine()
.context({
isValid: false,
errors: [] as string[],
})
.helper("addError", (context, error: string) => {
context.errors.push(error);
})
.rule(
"validate-age",
(user, { context, helpers }) => {
if (user.age < 18) {
helpers.addError("User must be at least 18 years old");
}
},
{ schema: userSchema },
)
.rule(
"validate-password",
(user, { context, helpers }) => {
if (!/[A-Z]/.test(user.password)) {
helpers.addError("Password must contain at least one uppercase letter");
}
if (!/[0-9]/.test(user.password)) {
helpers.addError("Password must contain at least one number");
}
},
{ schema: userSchema },
)
.rule("final-validation", (_, { context }) => {
context.isValid = context.errors.length === 0;
});
const session = engine.createSession();
session.insert({
id: "123",
email: "user@example.com",
age: 16,
password: "weak",
});
session.fire();
console.log(session.context);
// {
// isValid: false,
// errors: [
// "User must be at least 18 years old",
// "Password must contain at least one uppercase letter",
// "Password must contain at least one number"
// ]
// }