a reactive (signals based) hypermedia web framework (wip)
stormlightlabs.github.io/volt/
hypermedia
frontend
signals
1# Expression Evaluation
2
3VoltX.js evaluates JavaScript-like expressions in HTML templates using a cached `new Function()` compiler wrapped in a hardened scope proxy.
4The evaluator compiles each unique expression once, caches the resulting function, and executes it against a sandboxed scope that only exposes explicitly whitelisted globals.
5
6## Supported Syntax
7
8The expression language supports a subset of JavaScript:
9
10- Standard literals (numbers, strings, booleans, null, undefined)
11- Arithmetic operators (`+`, `-`, `*`, `/`, `%`)
12- Comparison operators (`===`, `!==`, `<`, `>`, `<=`, `>=`)
13- Logical operators (`&&`, `||`, `!`)
14- Ternary operator (`? :`).
15
16Property access works via dot notation (`user.name`) or bracket notation (`items[0]`).
17Method calls are supported on any object, including chaining (`text.trim().toUpperCase()`).
18Arrow functions work with single-expression bodies for use in array methods like `filter`, `map`, and `reduce`.
19
20Array and object literals can be created inline, with spread operator support (`...`) for both arrays and objects.
21Signals are automatically unwrapped when referenced in expressions.
22
23## Security Model
24
25The evaluator wraps each scope in an `Object.create(null)` proxy that filters dangerous identifiers, unwraps signals safely, and prevents prototype-chain access.
26Even though the implementation relies on `new Function()`, the compiled function only ever sees the proxy—never the real `globalThis`.
27
28### Blocked Access
29
30Three property names are unconditionally blocked to prevent prototype pollution: `__proto__`, `constructor`, and `prototype`.
31These restrictions apply to all access patterns including dot notation, bracket notation, and object literal keys.
32
33The following global names are blocked even if present in scope:
34`Function`, `eval`, `globalThis`, `window`, `global`, `process`, `require`, `import`, `module`, `exports`.
35
36### Allowed Operations
37
38Standard constructors and utilities remain accessible: `Array`, `Object`, `String`, `Number`, `Boolean`, `Date`, `Math`, `JSON`, `RegExp`, `Map`, `Set`, `Promise`.
39
40All built-in methods on native types (strings, arrays, objects, etc.) are permitted.
41Signal methods (`get`, `set`, `subscribe`) are explicitly allowed even though `constructor` is otherwise blocked.
42
43### Error Handling
44
45Expressions containing unsafe operations or syntax errors are wrapped in an `EvaluationError`.
46VoltX logs the error with the original expression for easier debugging and returns `undefined` to keep the UI responsive.
47
48Boolean negation is rewritten internally (`!foo` becomes `!$unwrap(foo)`) so signals behave like plain values during coercion without leaking signal internals into the template.
49
50## Guidelines
51
52### Performance
53
54Expressions are compiled on first use and subsequent evaluations hit the cache.
55Keep expressions simple and prefer computed signals for heavy logic—the evaluator already tracks dependencies so only affected bindings re-run.
56
57### Best Practices
58
59- Use computed signals for logic that appears in multiple bindings or involves expensive operations.
60- Never use untrusted user input directly in expressions without validation.
61- Prefer simple, readable expressions in templates over complex nested operations.
62- Structure your scope data with consistent shapes (or consistent types) to avoid runtime errors.
63- Remember that event handlers (`data-volt-on-*`) evaluate statements without unwrapping signals; call `signal.set(...)` or `store.set(...)` directly when you need to mutate state.