Skip to content

Commit e80859c

Browse files
authored
chore: perf docs (#19)
1 parent fc7cae7 commit e80859c

File tree

2 files changed

+76
-93
lines changed

2 files changed

+76
-93
lines changed

README.md

Lines changed: 75 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ Now we have solved this problem for you. We have designed a simple and easy-to-u
77
## Features
88

99
- 🔒 **Secure by default** - No access to global objects or prototype chain, does not use `eval` or `new Function`
10-
-**Async support** - All evaluation operations return Promises, supporting asynchronous computation and timeout protection
1110
- 🚀 **High performance** - Supports pre-compilation of expressions for improved performance with repeated evaluations
1211
- 🛠️ **Extensible** - Register custom functions to easily extend functionality
1312
- 🪩 **Lightweight** - Zero dependencies, small footprint
@@ -24,36 +23,54 @@ pnpm add @antv/expr
2423

2524
## Basic Usage
2625

27-
### Asynchronous Expression Evaluation
26+
### Synchronous Expression Evaluation
2827

2928
```typescript
3029
import { evaluate } from '@antv/expr';
3130

3231
// Basic evaluation
33-
const result = await evaluate('x + y', { x: 10, y: 20 }); // returns 30
32+
const result = evaluate('x + y', { x: 10, y: 20 }); // returns 30
3433

3534
// Using dot notation and array access
3635
const data = {
3736
values: [1, 2, 3],
3837
status: 'active'
3938
};
4039

41-
const result = await evaluate('data.values[0] + data.values[1]', { data }); // returns 3
40+
const result = evaluate('data.values[0] + data.values[1]', { data }); // returns 3
4241
```
4342

4443
### Pre-compiling Expressions
4544

4645
```typescript
47-
import { compileSync, compile } from '@antv/expr';
46+
import { compile } from '@antv/expr';
4847

49-
// Synchronous compilation (returns async execution function)
50-
const evaluator = compileSync('price * quantity');
51-
const result1 = await evaluator({ price: 10, quantity: 5 }); // returns 50
52-
const result2 = await evaluator({ price: 20, quantity: 3 }); // returns 60
48+
// Compile an expression
49+
const evaluator = compile('price * quantity');
50+
const result1 = evaluator({ price: 10, quantity: 5 }); // returns 50
51+
const result2 = evaluator({ price: 20, quantity: 3 }); // returns 60
52+
```
53+
54+
### Using Asynchronous Patterns (Optional)
55+
56+
While the library is synchronous, you can still use asynchronous patterns if needed:
57+
58+
```typescript
59+
import { evaluate } from '@antv/expr';
5360

54-
// Asynchronous compilation
55-
const asyncEvaluator = await compile('price * quantity');
56-
const result = await asyncEvaluator({ price: 15, quantity: 2 }); // returns 30
61+
// Wrap evaluation in an async function
62+
async function asyncEvaluate(expr, context) {
63+
return new Promise((resolve, reject) => {
64+
try {
65+
resolve(evaluate(expr, context));
66+
} catch (error) {
67+
reject(error);
68+
}
69+
});
70+
}
71+
72+
// Use with async/await
73+
const result = await asyncEvaluate('x + y', { x: 10, y: 20 }); // returns 30
5774
```
5875

5976
### Registering Custom Functions
@@ -66,8 +83,8 @@ register('sum', (...args) => args.reduce((a, b) => a + b, 0));
6683
register('average', (array) => array.reduce((a, b) => a + b, 0) / array.length);
6784

6885
// Use custom functions in expressions
69-
const result = await evaluate('@sum(1, 2, 3, 4)'); // returns 10
70-
const avg = await evaluate('@average(data.values)', {
86+
const result = evaluate('@sum(1, 2, 3, 4)'); // returns 10
87+
const avg = evaluate('@average(data.values)', {
7188
data: { values: [10, 20, 30, 40] }
7289
}); // returns 25
7390
```
@@ -78,18 +95,18 @@ const avg = await evaluate('@average(data.values)', {
7895

7996
```typescript
8097
// Simple variable reference
81-
const result = await evaluate('x', { x: 42 }); // returns 42
98+
const result = evaluate('x', { x: 42 }); // returns 42
8299

83100
// Nested property access with dot notation
84-
const result = await evaluate('user.profile.name', {
101+
const result = evaluate('user.profile.name', {
85102
user: { profile: { name: 'John' } }
86103
}); // returns 'John'
87104

88105
// Array access with bracket notation
89-
const result = await evaluate('items[0]', { items: [10, 20, 30] }); // returns 10
106+
const result = evaluate('items[0]', { items: [10, 20, 30] }); // returns 10
90107

91108
// Mixed dot and bracket notation
92-
const result = await evaluate('data.items[0].value', {
109+
const result = evaluate('data.items[0].value', {
93110
data: { items: [{ value: 42 }] }
94111
}); // returns 42
95112
```
@@ -98,28 +115,28 @@ const result = await evaluate('data.items[0].value', {
98115

99116
```typescript
100117
// Basic arithmetic
101-
const result = await evaluate('a + b * c', { a: 5, b: 3, c: 2 }); // returns 11
118+
const result = evaluate('a + b * c', { a: 5, b: 3, c: 2 }); // returns 11
102119

103120
// Using parentheses for grouping
104-
const result = await evaluate('(a + b) * c', { a: 5, b: 3, c: 2 }); // returns 16
121+
const result = evaluate('(a + b) * c', { a: 5, b: 3, c: 2 }); // returns 16
105122

106123
// Modulo operation
107-
const result = await evaluate('a % b', { a: 10, b: 3 }); // returns 1
124+
const result = evaluate('a % b', { a: 10, b: 3 }); // returns 1
108125
```
109126

110127
### Comparison and Logical Operations
111128

112129
```typescript
113130
// Comparison operators
114-
const result = await evaluate('age >= 18', { age: 20 }); // returns true
131+
const result = evaluate('age >= 18', { age: 20 }); // returns true
115132

116133
// Logical AND
117-
const result = await evaluate('isActive && !isDeleted', {
134+
const result = evaluate('isActive && !isDeleted', {
118135
isActive: true, isDeleted: false
119136
}); // returns true
120137

121138
// Logical OR
122-
const result = await evaluate('status === "active" || status === "pending"', {
139+
const result = evaluate('status === "active" || status === "pending"', {
123140
status: 'pending'
124141
}); // returns true
125142
```
@@ -128,12 +145,12 @@ const result = await evaluate('status === "active" || status === "pending"', {
128145

129146
```typescript
130147
// Simple ternary expression
131-
const result = await evaluate('age >= 18 ? "adult" : "minor"', {
148+
const result = evaluate('age >= 18 ? "adult" : "minor"', {
132149
age: 20
133150
}); // returns 'adult'
134151

135152
// Nested ternary expressions
136-
const result = await evaluate('score >= 90 ? "A" : score >= 80 ? "B" : "C"', {
153+
const result = evaluate('score >= 90 ? "A" : score >= 80 ? "B" : "C"', {
137154
score: 85
138155
}); // returns 'B'
139156
```
@@ -148,10 +165,10 @@ register('max', Math.max);
148165
register('formatCurrency', (amount) => `$${amount.toFixed(2)}`);
149166

150167
// Function call with arguments
151-
const result = await evaluate('@max(a, b, c)', { a: 5, b: 9, c: 2 }); // returns 9
168+
const result = evaluate('@max(a, b, c)', { a: 5, b: 9, c: 2 }); // returns 9
152169

153170
// Expression as function arguments
154-
const result = await evaluate('@formatCurrency(price * quantity)', {
171+
const result = evaluate('@formatCurrency(price * quantity)', {
155172
price: 10.5, quantity: 3
156173
}); // returns '$31.50'
157174
```
@@ -161,50 +178,32 @@ const result = await evaluate('@formatCurrency(price * quantity)', {
161178

162179
### Timeout Handling
163180

164-
```typescript
165-
import { configure, evaluate } from '@antv/expr';
166-
167-
// Register a setTimeout function
168-
register(
169-
"settimeout",
170-
(timeout: number) => new Promise((resolve) => setTimeout(resolve, timeout)),
171-
);
172-
173-
// Set maximum execution time to 500 milliseconds
174-
configure({ maxTimeout: 500 });
175-
176-
// Try to execute a potentially long-running expression
177-
try {
178-
await evaluate("@settimeout(1000)");
179-
} catch (error) {
180-
console.error("Expression error:", error.message); // Will catch timeout error: Expression error: Evaluation timed out after 500ms
181-
}
182-
```
183-
184-
### Security Configuration
181+
You can implement timeout handling by wrapping your evaluation in a Promise.race with a timeout:
185182

186183
```typescript
187-
import { configure } from '@antv/expr';
188-
189-
// Custom blacklisted keywords
190-
configure({
191-
blackList: new Set([
192-
'constructor',
193-
'__proto__',
194-
'prototype',
195-
'eval',
196-
'Function',
197-
'this',
198-
'window',
199-
'global',
200-
// Add custom blacklisted keywords
201-
'dangerous'
202-
])
203-
});
184+
import { evaluate } from "@antv/expr";
185+
186+
// Create a function that evaluates with a timeout
187+
function evaluateWithTimeout(expr, context, timeoutMs) {
188+
const evaluationPromise = new Promise((resolve, reject) => {
189+
try {
190+
const result = evaluate(expr, context);
191+
resolve(result);
192+
} catch (error) {
193+
reject(error);
194+
}
195+
});
196+
197+
const timeoutPromise = new Promise((_, reject) => {
198+
setTimeout(
199+
() => reject(new Error(`Evaluation timed out after ${timeoutMs}ms`)),
200+
timeoutMs,
201+
);
202+
});
203+
204+
return Promise.race([evaluationPromise, timeoutPromise]);
205+
}
204206
```
205-
**Default BlackList:** `['constructor', '__proto__', 'prototype', 'eval', 'Function', 'this', 'window', 'global']`
206-
**Note:** You will replace the default blacklist with your custom blacklist.
207-
208207
## Security Features
209208

210209
This library is designed with security in mind:
@@ -219,37 +218,22 @@ This library is designed with security in mind:
219218

220219
### Core Functions
221220

222-
#### `evaluate(expression: string, context?: object): Promise<any>`
221+
#### `evaluate(expression: string, context?: object): any`
223222

224-
Asynchronously evaluates an expression and returns the result.
223+
Synchronously evaluates an expression and returns the result.
225224

226225
- `expression`: The expression string to evaluate
227226
- `context`: An object containing variables used in the expression (optional)
228-
- Returns: A Promise that resolves to the result of the expression evaluation
227+
- Returns: The result of the expression evaluation
229228

230-
#### `compileSync(expression: string): (context?: object) => Promise<any>`
229+
#### `compile(expression: string): (context?: object) => any`
231230

232-
Synchronously compiles an expression, returning an async execution function that can be used multiple times.
231+
Synchronously compiles an expression, returning a function that can be used multiple times.
233232

234233
- `expression`: The expression string to compile
235-
- Returns: A function that accepts a context object and returns a Promise resolving to the evaluation result
234+
- Returns: A function that accepts a context object and returns the evaluation result
236235

237-
#### `compile(expression: string): Promise<(context?: object) => Promise<any>>`
238-
239-
Asynchronously compiles an expression, returning an async evaluation function that can be used multiple times.
240-
241-
- `expression`: The expression string to compile
242-
- Returns: A Promise that resolves to a function that accepts a context object and returns a Promise resolving to the evaluation result
243-
244-
### Configuration and Registration
245-
246-
#### `configure(config: Partial<ExpressionConfig>): void`
247-
248-
Sets global configuration for expression evaluation.
249-
250-
- `config`: Configuration options object
251-
- `maxTimeout`: Maximum execution time in milliseconds
252-
- `blackList`: Set of keywords not allowed in expressions
236+
### Registration
253237

254238
#### `register(name: string, fn: Function): void`
255239

@@ -258,7 +242,6 @@ Registers a custom function that can be used in expressions.
258242
- `name`: Function name (used with @ prefix in expressions)
259243
- `fn`: Function implementation
260244

261-
262245
### Error Handling
263246

264247
All evaluation errors throw an `ExpressionError` type exception with detailed error information.

tests/template-dynamic.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { compileSync, evaluate, register } from "../src";
2+
import { evaluate, register } from "../src";
33

44
describe("Dynamic Template Capabilities", () => {
55
// Helper function to evaluate expressions with custom functions

0 commit comments

Comments
 (0)