oneecma
An ECMAScript interpreter for .NET whose product is its error reports. When a script fails, it says what went wrong, where the failing value came from and how to fix it.
OneEcma is not affiliated with or endorsed by Ecma International. ECMAScript® is a registered trademark of Ecma International.
A quick look
checkout.js reads order.items, but the order it awaited holds lineItems. The report shows where order came from and suggests the fix. The headline is V8’s wording, so code that reads err.message keeps working.
var engine = new ScriptEngine(new ScriptEngineOptions { Limits = new EngineLimits { Fuel = 50_000_000, WallClockMs = 2_000 }, }); try { engine.Evaluate(File.ReadAllText("checkout.js"), "checkout.js"); } catch (JsException e) { Console.Error.WriteLine(DiagnosticRenderer.Render(e.Report!)); }
error[TypeError]: Cannot read properties of undefined (reading 'reduce') --> checkout.js:7:23 | 6 | const order = await loadOrder(7); | ------------------ `order` comes from here: { id, lineItems, total } 7 | const total = order.items.reduce((sum, i) => sum + i.price, 0); | ^^^^^ `order.items` is undefined | = help: did you mean `order.lineItems`? (Array, 2 items) = stack: main (checkout.js:7)
What it does
Reports that explain
Where the failing value came from, the locals of each frame, async stacks stitched across await, and suggestions drawn only from what the object holds. A run that succeeds pays nothing for them.
Sized for a model, or as JSON
A compact rendering fits an LLM’s context, and a versioned JSON report carries the same facts for tools. Syntax errors come all at once, with safe repairs if you ask.
Every script under limits
Fuel, memory, wall clock, call depth, string length and cancellation. The engine has no file, network, process or environment access of its own.
.NET bindings written at build time
Mark types [JsExport] and a source generator writes the marshalling, with no reflection. A wrong argument names the .NET member, the path and both types.
Record and replay
The clock, random numbers and host-call results are recorded, so a failed run replays anywhere with the same values and the same report.
ES2026, Intl and source maps
The ES2026 language and built-ins, Intl for six locales, and ECMA-426 source maps that point reports at the code you wrote.
test262, and Node byte for byte
On the pinned test262 suite, 22,973 language and 18,337 built-ins tests pass, with no unexpected failures and every documented failure listed with its reason. err.message and err.stack are compared with Node on a differential corpus. On lodash, preact and a React server render, each checked byte for byte against Node, it takes 0.62 to 1.08 times Jint’s time, on a fresh engine per run.
The packages
- RecursiveAI.OneEcma
The engine, Intl included:
ScriptEngine, the host API, limits, reports, source maps, record and replay. No dependency beyond the .NET base class library. 1.0.0. - .Generators
RecursiveAI.OneEcma.is theGenerators [JsExport]source generator. It runs in the compiler and adds nothing to your output. 1.0.0. - .Cli
RecursiveAI.OneEcma.is theCli oneecmacommand:run,checkandrepl. NativeAOT binaries for Windows, Linux and macOS are on the GitHub release. 1.0.0. - .NET
.NET 10, pure C#. NativeAOT and trimming are supported. Every package shares one version. MIT or Apache-2.0.