业务规则引擎
与框架无关、基于插件的规则引擎。AST 是唯一的事实来源:构建器编辑它,执行器遍历它,渲染器描述它,调试器解释它。不依赖付费 API、云服务或第三方引擎。
- 软件包
- 11
- 运算符
- 27
- 函数
- 23
- 规则类型
- 7
可导入格式 ADYSRE AST, jsonLogic, json-rules-engine, MongoDB query filters.
功能
A JSON tree
Rules are plain JSON. Storable, diffable, versionable, and readable by anything.
Everything is a plugin
Operators, functions, actions, fields, storage, renderers. Add one without touching the engine.
Runs anywhere
Synchronous, pure, zero dependencies. The same rule evaluates on a server, in a browser or in a test.
Says what it means
Every rule renders as a sentence, generated from the tree and never parsed back into it.
Shows its reasoning
Which row decided the verdict, what the operator received, and what short-circuiting hid.
Edits itself
A visual builder with undo, per-row validation and a live preview, driven entirely by the plugin metadata.
Takes what you have
Converts other rule formats, warning where an equivalent is near rather than exact.
Keeps its history
Versioning, restore and querying behind one contract every adapter is checked against.
试用
编辑任意条件,语句、判定和解释会同步更新。
A nested group, a date function, and two outcomes on one rule.
When
Then
Otherwise
In words
MatchedLarge orders from new customers need approval
Why this answer
Why this answer
MatchedTook 0.09msevery condition contributed, so no single one decided
1 branches never ran
- Matchedall of these are true0.07ms
- Matched
greaterThan0.02msThe operator received: 2400, 1000
- Matchedany of these are true0.03ms
- Matched
equals0.03msThe operator received: "new", "new"
- Never ran
before
The rule, as JSON
业务规则引擎使用指南
把规则引入项目所需的一切:安装、编写、运行、解释、扩展,以及让他人编辑。下面每段代码都可直接运行。
- 01
安装
按需引入。引擎与类型定义没有任何运行时依赖;构建器是独立的包,因为只做规则求值的服务器不必附带 React 编辑器。
terminal# the engine and its vocabulary pnpm add @adysre/rules-core @adysre/rules-types # the visual builder, when you want one pnpm add @adysre/rules-ui @adysre/rules-react @adysre/rules-theme
- 02
编写规则
规则就是普通 JSON。构建函数负责 ID 和时间戳,但你也可以手写对象、直接存储、在评审中做差异对比。
rules/large-orders.tsimport { all, condition, field, literal, rule } from '@adysre/rules-core'; // A rule is plain JSON. The builders exist so ids and timestamps are handled // for you, but nothing stops you writing the object by hand. const largeOrders = rule({ name: 'Large orders from new customers need approval', kind: 'validation', when: all([ condition({ left: field('order.total'), operator: 'greaterThan', args: [literal(1000)], }), ]), then: [{ id: 'a_hold', type: 'requireApproval' }], }); - 03
运行
同步且纯粹。注册表默认为空,因为规则能做什么由你决定。结果包含判定、动作和执行轨迹。
rules/run.tsimport { builtinPlugins, createContext, createRegistry, evaluateRule } from '@adysre/rules-core'; // The registry starts EMPTY: what rules may do is your choice. Pass the // built-ins for the usual twenty-seven operators and twenty-three functions. const registry = createRegistry(builtinPlugins); const outcome = evaluateRule(registry, largeOrders, createContext(order)); outcome.verdict; // 'matched' | 'unmatched' | 'skipped' | 'errored' outcome.actions; // what to do — the engine never does it for you outcome.trace; // every node that ran, and what it concluded - 04
读回来
每条规则都能渲染成一句话,由树生成,且永不反向解析。先有结构,后有文本。
rules/explain.tsimport { describeRule } from '@adysre/rules-renderer'; describeRule(largeOrders, { plugins: registry }).text; // When order total is greater than 1,000 // Then require approval // Structure first, text second: `.lines` gives typed segments, so a UI can // highlight the field being edited or colour the condition that decided it. - 05
添加自己的运算符
引擎能做的一切都是插件。注册之后构建器会提供它,调试器会解释它,而规则仍是普通 JSON。
rules/registry.tsimport { createRegistry, builtinPlugins, RuleError } from '@adysre/rules-core'; import type { OperatorPlugin } from '@adysre/rules-types'; // Your own operator. The builder picks it up, the debugger explains it, and // your rules stay plain JSON — no change to the engine. const withinBusinessHours: OperatorPlugin = { id: 'withinBusinessHours', labelKey: 'operators.withinBusinessHours', arity: 0, accepts: ['date'], evaluate: (left) => { if (typeof left !== 'string') { // Not `false`: a broken comparison must not look like a failed one. throw new RuleError('type_mismatch', 'withinBusinessHours needs a date.'); } const hour = new Date(left).getHours(); return hour >= 9 && hour < 17; }, }; const registry = createRegistry(builtinPlugins, { operators: [withinBusinessHours] }); - 06
让别人来编辑
可视化构建器完全由插件元数据驱动:arity 决定显示几个输入框,accepts 决定字段可用哪些运算符。
app/editor.tsx'use client'; import { RuleBuilder } from '@adysre/rules-ui'; export function Editor({ rule, onChange }) { return ( <RuleBuilder rule={rule} registry={registry} sample={sampleOrder} // runs the rule as you type onChange={onChange} /> ); } - 07
找出原因
调试器指出决定判定的那一行,并关闭短路再跑一次,暴露快速路径跳过的故障。
rules/debug.tsimport { debugRule } from '@adysre/rules-devtools'; const session = debugRule(registry, largeOrders, createContext(order)); session.decision; // which row decided, or that several did session.comparison.hiddenErrors; // faults a short circuit stepped over - 08
持久化
一个带版本与还原的存储契约。还原会写入新版本而不是回退历史。
rules/storage.tsimport { createMemoryStorage, runStorageConformance } from '@adysre/rules-storage'; const storage = createMemoryStorage(); await storage.save(largeOrders); // version 1 await storage.restore(largeOrders.id, 1); // a NEW version holding version 1 // Writing your own adapter? Check it against the same contract: const results = await runStorageConformance(() => createMyAdapter());
软件包
每个包都在需要它的阶段落地。按需安装。
- The vocabulary
@adysre/rules-typesType vocabulary for the ADYSRE Business Rules Engine: the rule AST, execution contracts and plugin interfaces.
v0.1.0 - The engine
@adysre/rules-coreFramework-agnostic core of the ADYSRE Business Rules Engine: AST builders, traversal and validation. No runtime dependencies.
v0.1.0 - Importers
@adysre/rules-parserImports rules into the ADYSRE AST from JSON, jsonLogic, json-rules-engine and MongoDB-style queries.
v0.1.0 - Natural language
@adysre/rules-rendererTurns an ADYSRE rule AST into readable language. Generated from the tree, never parsed back into it.
v0.1.0 - Persistence
@adysre/rules-storageWhere rules live: the storage contract, versioning, querying, and a reference adapter every other one has to match.
v0.1.0 - Builder state
@adysre/rules-reactHeadless state for a rule builder: an immutable store with history, plus the React bindings.
v0.1.0 - The visual builder
@adysre/rules-uiThe visual rule builder: React components over @adysre/rules-react, styled with design tokens.
v0.1.0 - The debugger
@adysre/rules-devtoolsThe execution debugger: why a rule answered what it answered, and what short-circuiting hid.
v0.1.0 - Over HTTP
@adysre/rules-nextRules over HTTP: route handlers and server actions on the Web standard, so they run in Next.js and anywhere else.
v0.1.0 - Design tokens
@adysre/rules-themeDesign tokens for the rule builder, and a contrast audit that proves a theme is readable.
v0.1.0 - Sandbox and examples
@adysre/rules-playgroundA runnable sandbox and the worked examples, each verified against the engine that runs it.
v0.1.0
值得了解的设计决策
The AST is the only source of truth
Everything else is a projection of it. Prose is generated from the tree and never parsed back, so the two can never disagree.
An error is not a false
A condition that cannot be evaluated is errored, and so is the rule. A broken comparison never looks like a failed one.
Actions are intent, never performed
The engine reports what should happen and the host decides. That is what lets a rule be run twice, in a preview, or in a test.
The registry cannot be mutated
Extending it returns a new one, so two tenants can hold different capabilities without one leaking into the other.
Every answer carries its reasoning
Each node records what it saw and what it concluded, so somebody can be shown the condition that decided it.
No language to learn
There is no text format to hand-author, so there is no second system to keep in step with the tree.