@bpmnkit/feel 0.0.8
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 +112 -0
- package/dist/ast.d.ts +111 -0
- package/dist/ast.js +2 -0
- package/dist/builtins.d.ts +10 -0
- package/dist/builtins.js +1194 -0
- package/dist/evaluator.d.ts +13 -0
- package/dist/evaluator.js +586 -0
- package/dist/formatter.d.ts +7 -0
- package/dist/formatter.js +120 -0
- package/dist/highlighter.d.ts +14 -0
- package/dist/highlighter.js +149 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +12 -0
- package/dist/lexer.d.ts +9 -0
- package/dist/lexer.js +161 -0
- package/dist/parser.d.ts +13 -0
- package/dist/parser.js +853 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +129 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/bpmn-sdk/monorepo/main/doc/logos/logo-2-gateway.svg" width="72" height="72" alt="BPMN Kit logo">
|
|
3
|
+
<h1>@bpmnkit/feel</h1>
|
|
4
|
+
<p>Complete FEEL (Friendly Enough Expression Language) implementation — parser, evaluator, and highlighter</p>
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/@bpmnkit/feel)
|
|
7
|
+
[](https://github.com/bpmnkit/monorepo/blob/main/LICENSE)
|
|
8
|
+
[](https://github.com/bpmnkit/monorepo)
|
|
9
|
+
|
|
10
|
+
[Documentation](https://bpmn-sdk-docs.pages.dev) · [GitHub](https://github.com/bpmnkit/monorepo) · [Changelog](https://github.com/bpmnkit/monorepo/blob/main/packages/feel/CHANGELOG.md)
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
`@bpmnkit/feel` is a complete implementation of the FEEL expression language used in DMN decision tables and BPMN condition expressions. It includes a tokenizer, recursive-descent parser, AST evaluator, formatter, and syntax highlighter.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Full FEEL grammar** — arithmetic, comparisons, logic, function calls, paths, filters
|
|
22
|
+
- **Temporal types** — date, time, datetime, duration (ISO 8601, full spec compliance)
|
|
23
|
+
- **Unary tests** — DMN input expression syntax (`> 5`, `"gold", "silver"`, `[1..10]`)
|
|
24
|
+
- **Built-in functions** — 50+ standard FEEL functions (string, list, numeric, date, context)
|
|
25
|
+
- **Range expressions** — `[1..10]`, `(0..1)`, `[today..end]`
|
|
26
|
+
- **Context literals** — `{ key: value, nested: { x: 1 } }`
|
|
27
|
+
- **Syntax highlighting** — semantic token classification for editors
|
|
28
|
+
- **Zero dependencies**
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm install @bpmnkit/feel
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
### Evaluate an expression
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { parseExpression, evaluate } from "@bpmnkit/feel"
|
|
42
|
+
|
|
43
|
+
const parsed = parseExpression("amount * 1.2 + fee")
|
|
44
|
+
if (!parsed.errors.length) {
|
|
45
|
+
const result = evaluate(parsed.ast!, { amount: 100, fee: 5 })
|
|
46
|
+
console.log(result) // 125
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Evaluate unary tests (DMN input expressions)
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { parseUnaryTests, evaluateUnaryTests } from "@bpmnkit/feel"
|
|
54
|
+
|
|
55
|
+
// Does input value match any listed condition?
|
|
56
|
+
const parsed = parseUnaryTests('"gold","silver"')
|
|
57
|
+
const matches = evaluateUnaryTests(parsed.ast!, "gold", { /* context */ })
|
|
58
|
+
console.log(matches) // true
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Syntax highlighting
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { highlightFeel } from "@bpmnkit/feel"
|
|
65
|
+
|
|
66
|
+
const tokens = highlightFeel('if x > 10 then "high" else "low"')
|
|
67
|
+
for (const token of tokens) {
|
|
68
|
+
console.log(token.type, token.value) // keyword, number, string, ...
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## API Reference
|
|
73
|
+
|
|
74
|
+
| Export | Description |
|
|
75
|
+
|--------|-------------|
|
|
76
|
+
| `parseExpression(src)` | Parse a FEEL expression → `ParseResult` |
|
|
77
|
+
| `parseUnaryTests(src)` | Parse unary tests → `ParseResult` |
|
|
78
|
+
| `evaluate(ast, ctx)` | Evaluate a parsed expression |
|
|
79
|
+
| `evaluateUnaryTests(ast, input, ctx)` | Test input against unary tests |
|
|
80
|
+
| `formatFeel(src)` | Pretty-print a FEEL expression |
|
|
81
|
+
| `highlightFeel(src)` | Tokenize with semantic types for highlighting |
|
|
82
|
+
| `tokenize(src)` | Raw token stream |
|
|
83
|
+
| `annotate(src)` | Full AST with position metadata |
|
|
84
|
+
|
|
85
|
+
### ParseResult
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
interface ParseResult {
|
|
89
|
+
ast: FeelNode | null
|
|
90
|
+
errors: ParseError[] // { message, position }
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Related Packages
|
|
97
|
+
|
|
98
|
+
| Package | Description |
|
|
99
|
+
|---------|-------------|
|
|
100
|
+
| [`@bpmnkit/core`](https://www.npmjs.com/package/@bpmnkit/core) | BPMN/DMN/Form parser, builder, layout engine |
|
|
101
|
+
| [`@bpmnkit/canvas`](https://www.npmjs.com/package/@bpmnkit/canvas) | Zero-dependency SVG BPMN viewer |
|
|
102
|
+
| [`@bpmnkit/editor`](https://www.npmjs.com/package/@bpmnkit/editor) | Full-featured interactive BPMN editor |
|
|
103
|
+
| [`@bpmnkit/engine`](https://www.npmjs.com/package/@bpmnkit/engine) | Lightweight BPMN process execution engine |
|
|
104
|
+
| [`@bpmnkit/plugins`](https://www.npmjs.com/package/@bpmnkit/plugins) | 22 composable canvas plugins |
|
|
105
|
+
| [`@bpmnkit/api`](https://www.npmjs.com/package/@bpmnkit/api) | Camunda 8 REST API TypeScript client |
|
|
106
|
+
| [`@bpmnkit/ascii`](https://www.npmjs.com/package/@bpmnkit/ascii) | Render BPMN diagrams as Unicode ASCII art |
|
|
107
|
+
| [`@bpmnkit/profiles`](https://www.npmjs.com/package/@bpmnkit/profiles) | Shared auth, profile storage, and client factories for CLI & proxy |
|
|
108
|
+
| [`@bpmnkit/operate`](https://www.npmjs.com/package/@bpmnkit/operate) | Monitoring & operations frontend for Camunda clusters |
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
[MIT](https://github.com/bpmnkit/monorepo/blob/main/LICENSE) © bpmn-sdk
|
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export type BinaryOp = "+" | "-" | "*" | "/" | "**" | "=" | "!=" | "<" | "<=" | ">" | ">=" | "and" | "or";
|
|
2
|
+
export type FeelNode = {
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
} & FeelNodeKind;
|
|
6
|
+
type FeelNodeKind = {
|
|
7
|
+
kind: "number";
|
|
8
|
+
value: number;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "string";
|
|
11
|
+
value: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "boolean";
|
|
14
|
+
value: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "null";
|
|
17
|
+
} | {
|
|
18
|
+
kind: "temporal";
|
|
19
|
+
raw: string;
|
|
20
|
+
} | {
|
|
21
|
+
kind: "name";
|
|
22
|
+
name: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: "list";
|
|
25
|
+
items: FeelNode[];
|
|
26
|
+
} | {
|
|
27
|
+
kind: "context";
|
|
28
|
+
entries: Array<{
|
|
29
|
+
key: string;
|
|
30
|
+
value: FeelNode;
|
|
31
|
+
}>;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "range";
|
|
34
|
+
startIncluded: boolean;
|
|
35
|
+
low: FeelNode;
|
|
36
|
+
high: FeelNode;
|
|
37
|
+
endIncluded: boolean;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "unary-minus";
|
|
40
|
+
operand: FeelNode;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "binary";
|
|
43
|
+
op: BinaryOp;
|
|
44
|
+
left: FeelNode;
|
|
45
|
+
right: FeelNode;
|
|
46
|
+
} | {
|
|
47
|
+
kind: "path";
|
|
48
|
+
base: FeelNode;
|
|
49
|
+
key: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "filter";
|
|
52
|
+
base: FeelNode;
|
|
53
|
+
condition: FeelNode;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "call";
|
|
56
|
+
callee: string;
|
|
57
|
+
args: FeelNode[];
|
|
58
|
+
} | {
|
|
59
|
+
kind: "call-named";
|
|
60
|
+
callee: string;
|
|
61
|
+
args: Array<{
|
|
62
|
+
name: string;
|
|
63
|
+
value: FeelNode;
|
|
64
|
+
}>;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "if";
|
|
67
|
+
condition: FeelNode;
|
|
68
|
+
then: FeelNode;
|
|
69
|
+
else: FeelNode;
|
|
70
|
+
} | {
|
|
71
|
+
kind: "for";
|
|
72
|
+
bindings: Array<{
|
|
73
|
+
name: string;
|
|
74
|
+
domain: FeelNode;
|
|
75
|
+
}>;
|
|
76
|
+
body: FeelNode;
|
|
77
|
+
} | {
|
|
78
|
+
kind: "some" | "every";
|
|
79
|
+
bindings: Array<{
|
|
80
|
+
name: string;
|
|
81
|
+
domain: FeelNode;
|
|
82
|
+
}>;
|
|
83
|
+
satisfies: FeelNode;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "between";
|
|
86
|
+
value: FeelNode;
|
|
87
|
+
low: FeelNode;
|
|
88
|
+
high: FeelNode;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "in-test";
|
|
91
|
+
value: FeelNode;
|
|
92
|
+
test: FeelNode;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "instance-of";
|
|
95
|
+
value: FeelNode;
|
|
96
|
+
typeName: string;
|
|
97
|
+
} | {
|
|
98
|
+
kind: "function-def";
|
|
99
|
+
params: string[];
|
|
100
|
+
body: FeelNode;
|
|
101
|
+
} | {
|
|
102
|
+
kind: "unary-test-list";
|
|
103
|
+
tests: FeelNode[];
|
|
104
|
+
} | {
|
|
105
|
+
kind: "unary-not";
|
|
106
|
+
tests: FeelNode[];
|
|
107
|
+
} | {
|
|
108
|
+
kind: "any-input";
|
|
109
|
+
};
|
|
110
|
+
export {};
|
|
111
|
+
//# sourceMappingURL=ast.d.ts.map
|
package/dist/ast.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FeelFunction, FeelValue } from "./types.js";
|
|
2
|
+
declare function parseTemporal(raw: string): FeelValue;
|
|
3
|
+
declare function compareValues(a: FeelValue, b: FeelValue): number | null;
|
|
4
|
+
/** Look up a built-in function by name. Returns undefined if not found. */
|
|
5
|
+
export declare function getBuiltin(name: string): FeelFunction | undefined;
|
|
6
|
+
/** All built-in names. */
|
|
7
|
+
export declare function builtinNames(): string[];
|
|
8
|
+
/** Parse a @"..." temporal literal to a FeelValue. */
|
|
9
|
+
export { parseTemporal, compareValues };
|
|
10
|
+
//# sourceMappingURL=builtins.d.ts.map
|