@criterionx/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -83
- package/package.json +14 -20
package/README.md
CHANGED
|
@@ -1,30 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="criterion.jpg" alt="Criterion" width="100%" />
|
|
3
|
-
</p>
|
|
1
|
+
# @criterionx/core
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
<strong>A universal, deterministic, and explainable decision engine for business-critical systems.</strong>
|
|
7
|
-
</p>
|
|
8
|
-
|
|
9
|
-
<p align="center">
|
|
10
|
-
<a href="https://www.npmjs.com/package/@criterionx/core"><img src="https://img.shields.io/npm/v/@criterionx/core.svg" alt="npm version"></a>
|
|
11
|
-
<a href="https://github.com/tomymaritano/criterionx/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@criterionx/core.svg" alt="license"></a>
|
|
12
|
-
<a href="https://tomymaritano.github.io/criterionx/"><img src="https://img.shields.io/badge/docs-vitepress-brightgreen.svg" alt="docs"></a>
|
|
13
|
-
</p>
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## What is Criterion?
|
|
18
|
-
|
|
19
|
-
Criterion helps you encode business decisions as **pure, testable functions** with built-in validation and explainability.
|
|
20
|
-
|
|
21
|
-
Instead of scattering `if/else` statements across your codebase, you define decisions declaratively:
|
|
22
|
-
|
|
23
|
-
- **"Should this transaction be flagged as high-risk?"**
|
|
24
|
-
- **"Is this user eligible for a premium tier?"**
|
|
25
|
-
- **"What discount applies to this order?"**
|
|
26
|
-
|
|
27
|
-
Every decision returns not just a result, but a complete explanation of *why* that result was reached — perfect for audits, debugging, and compliance.
|
|
3
|
+
Universal decision engine for business-critical decisions.
|
|
28
4
|
|
|
29
5
|
## Installation
|
|
30
6
|
|
|
@@ -42,14 +18,15 @@ const riskDecision = defineDecision({
|
|
|
42
18
|
id: "transaction-risk",
|
|
43
19
|
version: "1.0.0",
|
|
44
20
|
inputSchema: z.object({ amount: z.number() }),
|
|
45
|
-
outputSchema: z.object({ risk: z.enum(["HIGH", "
|
|
21
|
+
outputSchema: z.object({ risk: z.enum(["HIGH", "LOW"]) }),
|
|
46
22
|
profileSchema: z.object({ threshold: z.number() }),
|
|
47
23
|
rules: [
|
|
48
24
|
{
|
|
49
25
|
id: "high-risk",
|
|
50
26
|
when: (input, profile) => input.amount > profile.threshold,
|
|
51
27
|
emit: () => ({ risk: "HIGH" }),
|
|
52
|
-
explain: (input, profile) =>
|
|
28
|
+
explain: (input, profile) =>
|
|
29
|
+
`Amount ${input.amount} exceeds threshold ${profile.threshold}`,
|
|
53
30
|
},
|
|
54
31
|
{
|
|
55
32
|
id: "low-risk",
|
|
@@ -67,77 +44,58 @@ const result = engine.run(
|
|
|
67
44
|
{ profile: { threshold: 10000 } }
|
|
68
45
|
);
|
|
69
46
|
|
|
70
|
-
console.log(result.data);
|
|
71
|
-
console.log(engine.explain(result));
|
|
72
|
-
// Decision: transaction-risk v1.0.0
|
|
73
|
-
// Status: OK
|
|
74
|
-
// Matched: high-risk
|
|
75
|
-
// Reason: Amount 15000 > 10000
|
|
47
|
+
console.log(result.data); // { risk: "HIGH" }
|
|
76
48
|
```
|
|
77
49
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
- **Pure & Deterministic** — Same input always produces the same output
|
|
81
|
-
- **Fully Explainable** — Every decision includes a complete audit trail
|
|
82
|
-
- **Contract-First** — Zod schemas validate inputs, outputs, and profiles
|
|
83
|
-
- **Profile-Driven** — Parameterize decisions by region, tier, or environment
|
|
84
|
-
- **Zero Side Effects** — No I/O, no database, no external calls
|
|
85
|
-
- **Testable by Design** — Pure functions are trivial to test
|
|
86
|
-
|
|
87
|
-
## Documentation
|
|
50
|
+
---
|
|
88
51
|
|
|
89
|
-
|
|
52
|
+
## Architectural Invariants
|
|
90
53
|
|
|
91
|
-
-
|
|
92
|
-
- [Core Concepts](https://tomymaritano.github.io/criterionx/guide/core-concepts)
|
|
93
|
-
- [API Reference](https://tomymaritano.github.io/criterionx/api/engine)
|
|
94
|
-
- [Examples](https://tomymaritano.github.io/criterionx/examples/currency-risk)
|
|
54
|
+
> **This package is HTTP-agnostic. It must never depend on server concerns.**
|
|
95
55
|
|
|
96
|
-
|
|
56
|
+
### Non-Negotiable Principles
|
|
97
57
|
|
|
98
|
-
|
|
58
|
+
1. **Pure and Deterministic**
|
|
59
|
+
- Same input + same profile = same output, always
|
|
60
|
+
- No hidden state, no side effects
|
|
99
61
|
|
|
100
|
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
62
|
+
2. **No I/O in Rules**
|
|
63
|
+
- Rules cannot fetch data
|
|
64
|
+
- Rules cannot make network calls
|
|
65
|
+
- Rules cannot read from filesystem
|
|
66
|
+
- All data must be passed in as context
|
|
105
67
|
|
|
106
|
-
|
|
68
|
+
3. **Explicit Dependencies**
|
|
69
|
+
- No auto-discovery
|
|
70
|
+
- No magic imports
|
|
71
|
+
- Decisions are explicitly passed to consumers
|
|
107
72
|
|
|
108
|
-
|
|
73
|
+
4. **Validation at Boundaries**
|
|
74
|
+
- Input validated via Zod schema
|
|
75
|
+
- Output validated via Zod schema
|
|
76
|
+
- Profile validated via Zod schema
|
|
109
77
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{ id: "rule-2", when: (i) => i.x > 50, emit: ..., explain: ... },
|
|
114
|
-
{ id: "default", when: () => true, emit: ..., explain: ... },
|
|
115
|
-
]
|
|
116
|
-
```
|
|
78
|
+
5. **Explainability**
|
|
79
|
+
- Every rule has an `explain` function
|
|
80
|
+
- Every result includes full audit trace
|
|
117
81
|
|
|
118
|
-
###
|
|
82
|
+
### What This Package Does NOT Do
|
|
119
83
|
|
|
120
|
-
|
|
84
|
+
- HTTP/REST handling (use `@criterionx/server`)
|
|
85
|
+
- Database operations
|
|
86
|
+
- File system access
|
|
87
|
+
- Network requests
|
|
88
|
+
- Caching
|
|
89
|
+
- Authentication/Authorization
|
|
121
90
|
|
|
122
|
-
|
|
123
|
-
// Same decision, different thresholds
|
|
124
|
-
engine.run(decision, input, { profile: usProfile });
|
|
125
|
-
engine.run(decision, input, { profile: euProfile });
|
|
126
|
-
```
|
|
91
|
+
This is intentional. The core is a knife, not a Swiss Army knife.
|
|
127
92
|
|
|
128
|
-
|
|
93
|
+
---
|
|
129
94
|
|
|
130
|
-
|
|
131
|
-
- A machine learning framework
|
|
132
|
-
- A data pipeline
|
|
133
|
-
- A plugin marketplace
|
|
95
|
+
## Documentation
|
|
134
96
|
|
|
135
|
-
|
|
97
|
+
Full documentation: [https://tomymaritano.github.io/criterionx/](https://tomymaritano.github.io/criterionx/)
|
|
136
98
|
|
|
137
99
|
## License
|
|
138
100
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
## Author
|
|
142
|
-
|
|
143
|
-
**Tomas Maritano** — [@tomymaritano](https://github.com/tomymaritano)
|
|
101
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@criterionx/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Universal decision engine for business-critical decisions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,18 +16,6 @@
|
|
|
16
16
|
"LICENSE",
|
|
17
17
|
"README.md"
|
|
18
18
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"test:watch": "vitest",
|
|
23
|
-
"test:coverage": "vitest run --coverage",
|
|
24
|
-
"typecheck": "tsc --noEmit",
|
|
25
|
-
"benchmark": "tsx benchmarks/run.ts",
|
|
26
|
-
"prepublishOnly": "npm run build",
|
|
27
|
-
"docs:dev": "vitepress dev docs",
|
|
28
|
-
"docs:build": "vitepress build docs",
|
|
29
|
-
"docs:preview": "vitepress preview docs"
|
|
30
|
-
},
|
|
31
19
|
"keywords": [
|
|
32
20
|
"decision-engine",
|
|
33
21
|
"rules-engine",
|
|
@@ -46,25 +34,31 @@
|
|
|
46
34
|
"license": "MIT",
|
|
47
35
|
"repository": {
|
|
48
36
|
"type": "git",
|
|
49
|
-
"url": "https://github.com/tomymaritano/criterionx.git"
|
|
37
|
+
"url": "https://github.com/tomymaritano/criterionx.git",
|
|
38
|
+
"directory": "packages/core"
|
|
50
39
|
},
|
|
51
40
|
"bugs": {
|
|
52
41
|
"url": "https://github.com/tomymaritano/criterionx/issues"
|
|
53
42
|
},
|
|
54
43
|
"homepage": "https://github.com/tomymaritano/criterionx#readme",
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"zod": "^3.22.0"
|
|
46
|
+
},
|
|
55
47
|
"devDependencies": {
|
|
56
48
|
"@vitest/coverage-v8": "^2.0.0",
|
|
57
49
|
"tsup": "^8.0.0",
|
|
58
|
-
"tsx": "^4.7.0",
|
|
59
50
|
"typescript": "^5.3.0",
|
|
60
|
-
"vitepress": "^1.6.4",
|
|
61
51
|
"vitest": "^2.0.0",
|
|
62
52
|
"zod": "^3.22.0"
|
|
63
53
|
},
|
|
64
|
-
"peerDependencies": {
|
|
65
|
-
"zod": "^3.22.0"
|
|
66
|
-
},
|
|
67
54
|
"engines": {
|
|
68
55
|
"node": ">=18"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"test:coverage": "vitest run --coverage",
|
|
62
|
+
"typecheck": "tsc --noEmit"
|
|
69
63
|
}
|
|
70
|
-
}
|
|
64
|
+
}
|