Alethic.Node
1.0.0
See the version list below for details.
dotnet add package Alethic.Node --version 1.0.0
NuGet\Install-Package Alethic.Node -Version 1.0.0
<PackageReference Include="Alethic.Node" Version="1.0.0" />
<PackageVersion Include="Alethic.Node" Version="1.0.0" />
<PackageReference Include="Alethic.Node" />
paket add Alethic.Node --version 1.0.0
#r "nuget: Alethic.Node, 1.0.0"
#:package Alethic.Node@1.0.0
#addin nuget:?package=Alethic.Node&version=1.0.0
#tool nuget:?package=Alethic.Node&version=1.0.0
Alethic.Node
Node embedded in a .NET process, through node-api-dotnet. No sidecar process, no IPC. It is libnode, and the package says so: there is no runtime abstraction here.
For any host: .NET 8 and 9, and .NET Framework 4.7.2 and later, so an ASP.NET Web Forms application can use it as well as a console or a service. Alethic.Node.AspNetCore builds server-side rendering for ASP.NET Core on it.
Two things
Engines run JavaScript. A NodeEnginePool holds several, each a libnode runtime on its own thread. It owns the
threads, and the whole application shares one.
Modules are Node's. A module is loaded with require and cached in require.cache by resolved filename, so it
evaluates once per engine and keeps its module scope — the identity a module has in any other Node program. This
library caches nothing of its own.
services.AddNodeEnginePool(o =>
{
o.EngineCount = 4; // must track the CPU limit; see remarks on the option
o.MaxConcurrencyPerEngine = 4; // backpressure, not mutual exclusion
});
var pool = provider.GetRequiredService<NodeEnginePool>();
var result = await pool.RunAsync(NodeModuleSource.FromFile("tool.cjs"), async exports =>
(int)await ((JSPromise)exports.CallMethod("transform", input)).AsTask());
A one-shot puts you on an engine's thread writing ordinary node-api-dotnet. For several steps that must share one
engine — or a claim that outlives a single call — take a lease instead: await using var lease = await pool.AcquireAsync(); and run against it. A lease is a capacity claim and an affinity pin, not exclusivity; engines
overlap many concurrent calls anyway.
Without a container, construct the pool from its options, a logger factory and a service provider —
NullLoggerFactory.Instance, and whatever IServiceProvider the host has, which ConfigureEngine is handed — and
dispose of it when the host stops.
Constraints worth knowing
- Reference the RID-specific
Microsoft.JavaScript.LibNode.<rid>package for each runtime you deploy to. The umbrella package depends on every platform at once and lands ~640 MB of native libraries in the output. Where the library is neither beside the application nor underruntimes/<rid>/native— a web application on .NET Framework, whose base directory is the site and notbin— setLibNodePath. - Engine count must be configured, never derived. Inside a container the processor count reports the host's cores, not the quota. One engine already overlaps many concurrent calls, because everything a module awaits yields to its event loop; engines exist for CPU parallelism.
- One Node per process. Node starts once per process and cannot start again, so every pool in a process shares one platform. Where a process holds more than one copy of this assembly — ASP.NET on .NET Framework restarting an application in a new AppDomain of the same process — a copy that finds the library already loaded by another refuses to start, on Windows, rather than fail natively. Keep such hosts from restarting applications in place.
- CommonJS only. The embedded runtime registers no dynamic-import callback, so ES modules and
import()do not resolve. Bundle fully static — for esbuild,--format=cjswith code splitting off. - Module scope is shared across concurrent calls on an engine, since the module is loaded once and reused.
Per-call state belongs in the call —
AsyncLocalStorage, say — not at module scope. - A rebuilt module is not picked up.
require.cacheholds a module for the runtime's life, so rebuilding while the host runs changes nothing until restart.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.2
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.JavaScript.NodeApi (>= 0.9.26)
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.JavaScript.NodeApi (>= 0.9.26)
-
net9.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.0)
- Microsoft.Extensions.Logging (>= 9.0.0)
- Microsoft.Extensions.Options (>= 9.0.0)
- Microsoft.JavaScript.NodeApi (>= 0.9.26)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Alethic.Node:
| Package | Downloads |
|---|---|
|
Alethic.Node.Http
Serving a JavaScript application over HTTP from Node embedded in a .NET process, whatever the .NET host: the fetch(request, env, ctx) protocol, as Alethic.Node.AspNetCore and Alethic.Node.AspNet both speak it. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.1-pre.27 | 70 | 10/4/2026 |
| 1.0.1-pre.25 | 78 | 10/4/2026 |
| 1.0.0 | 240 | 10/3/2026 |
| 1.0.0-pre.37 | 83 | 10/3/2026 |
| 1.0.0-pre.34 | 75 | 10/3/2026 |
| 1.0.0-pre.14 | 63 | 10/3/2026 |