JavaScript/TypeScript execution library for .NET, built on Jint.
- JavaScript execution via Jint (ES2025 support)
- Execution methods:
ExecuteAsync(string)/ExecuteAsync(prepared module)(standard),Evaluate(string)/Evaluate(prepared),EvaluateAsync() - Pre-parsed scripts (
Prepare()/PrepareModule()) for maximum throughput Sandboxed()+AllowOnly/DenyTypesfor untrusted rules with real CLR objects- TypeScript 6.0 transpilation with embedded compiler
fetch()API with opt-in sandboxing- Automatic .NET
Task→ JSPromiseinterop console.log/warn/error/debugviaILoggersetTimeout/setIntervalsupport- Extensible module system (HTTP, Database, SMTP, Templates, and more)
.d.tsgeneration for IntelliSense support- Built for .NET 10
dotnet add package Cocoar.JsEval.Engineservices.AddJsEval();varengine=sp.GetRequiredService<JsEngine>();engine.SetValue("name","World");engine.Evaluate("var greeting = 'Hello, ' + name + '!';");varresult=engine.GetValue<string>("greeting");// "Hello, World!"Two independent axes, and an untrusted script wants both. Sandboxed() governs
what a script can do on its own — strict mode, no eval/Function, no
reflection, and memory, recursion, stack, array and regex limits. AllowOnly
and DenyTypes govern what it can reach: passing an object otherwise grants
everything reachable from it.
services.AddJsEval(b =>b.Sandboxed()// hardens the runtime, latches.AllowOnly(a =>a.Member((Customerc)=>c.Name).Member((Customerc)=>c.Age)).DenyTypes(typeof(DbContext),typeof(IServiceProvider)));Sandboxed() latches in both directions — EnableFetch() before or after it
throws — so the guarantee never depends on the order the builder is written in.
It hardens the runtime but does not narrow the object graph; that is what
AllowOnly is for. Isolation between scripts is the engine instance: resolve a
separate engine per script source.
services.AddJsEval(b =>b.AddModule<CommonModule>().AddModule<HttpModule>());varengine=sp.GetRequiredService<JsEngine>();awaitengine.ExecuteAsync(@" import * as common from 'common'; export function newId() { return common.Guid.New().toString(); }");varid=engine.InvokeFunction("newId");// fresh GUID each callES-module semantics: top-level code runs once per unique script on a given engine — repeated
ExecuteAsynccalls return the cached module namespace. Put per-call work inside exported functions and invoke them viaInvokeFunction. For true per-call re-execution use the lightweightEvaluate(string)path.
// Parse once (thread-safe, cacheable)varprepared=JsEngine.Prepare("query.WhereResponsible(ctx.UserId);");// Execute many times — no re-parsingengine.SetValue("ctx",accessContext);engine.SetValue("query",queryBuilder);engine.Evaluate(prepared);dotnet add package Cocoar.JsEval.TypeScriptservices.AddTsTranspiler();vartranspiler=sp.GetRequiredService<TsTranspiler>();varjs=transpiler.Transpile(tsCode);// transpile onceawaitengine.ExecuteAsync(js);// executeservices.AddJsEval(b =>b.EnableFetch());constresponse=awaitfetch('https://api.example.com/data');constbody=awaitresponse.text();console.log(response.status,response.ok);engine.SetValue("loadData",newFunc<string,Task<string>>(async id =>{returnawaitdb.FindAsync(id);}));constdata=awaitloadData('item-123');// .NET Task becomes a Promise| Method | Module System | async | Prepared | Use Case |
|---|---|---|---|---|
ExecuteAsync(string) | Yes | Yes | No | Standard -- use when you don't know what's in the script |
ExecuteAsync(JsPreparedModule) | Yes | Yes | Yes | Pre-parsed module -- reuse across calls |
Evaluate(string) | No | No | No | Lightweight sync -- when you control the script |
Evaluate(JsPreparedScript) | No | No | Yes | Max performance -- pre-parsed, reusable, no module system |
EvaluateAsync(string) | No | Yes | No | Lightweight async -- no modules but needs await |
ExecuteAsync is the default/standard method for trusted integration scripts and provides the full module system. For tenant- or end-user-authored rules, resolve a Sandboxed() engine with an AllowOnly surface. Evaluate is a lightweight execution mode for scripts you control and know do not need modules.
| Package | Description |
|---|---|
Cocoar.JsEval | Core: interfaces, helpers, JsFunction |
Cocoar.JsEval.Engine | JsEngine + fetch() + DI registration |
Cocoar.JsEval.TypeScript | TypeScript 6.0 transpiler |
Cocoar.JsEval.TsDefinition | .d.ts generation for IntelliSense |
Cocoar.JsEval.Linq | JS arrow functions → real Expression trees for Marten / EF / LINQ2DB |
Cocoar.JsEval.Module.Common | Guid, Sleep, Random |
Cocoar.JsEval.Module.Http | Fluent HTTP client |
Cocoar.JsEval.Module.Database | SQL Server + PostgreSQL |
Cocoar.JsEval.Module.Smtp | Email via MailKit |
Cocoar.JsEval.Module.AngleSharp | HTML parsing |
Cocoar.JsEval.Module.Template | Scriban templates |
Cocoar.JsEval.Module.Logging | Microsoft.Extensions.Logging |
Cocoar.JsEval.Module.VirtualFileSystem | Zio VFS |
Apache-2.0 — COCOAR e.U.