WACS.WASI.Preview2
0.5.0
dotnet add package WACS.WASI.Preview2 --version 0.5.0
NuGet\Install-Package WACS.WASI.Preview2 -Version 0.5.0
<PackageReference Include="WACS.WASI.Preview2" Version="0.5.0" />
<PackageVersion Include="WACS.WASI.Preview2" Version="0.5.0" />
<PackageReference Include="WACS.WASI.Preview2" />
paket add WACS.WASI.Preview2 --version 0.5.0
#r "nuget: WACS.WASI.Preview2, 0.5.0"
#:package WACS.WASI.Preview2@0.5.0
#addin nuget:?package=WACS.WASI.Preview2&version=0.5.0
#tool nuget:?package=WACS.WASI.Preview2&version=0.5.0
WACS.WASI.Preview2
WASI Preview 2 (component-model) host implementations for the WACS
WebAssembly interpreter. Implements the canonical-ABI surface for
wasi:cli, wasi:clocks, wasi:filesystem, wasi:http, wasi:io,
wasi:random, and wasi:sockets — all of WASI 0.2.3.
Architecture
Three layers:
- Vendored WIT files at
wit/— the WASI 0.2.3 specs, sourced from upstreamwasi-cli. Drives both code generation and contract validation. - Generated host interfaces — a Roslyn source generator
(
Wacs.ComponentModel.Bindgen.SourceGen) reads the vendored WIT at compile time and emits onepublic interface IXxxper WIT resource or free-function group, with[WitSource]attributes carrying the source WIT text. See../../../Wacs.ComponentModel/Wacs.ComponentModel.Bindgen.SourceGen/README.md. *Bindings.csorchestrators — per-subsystemIBindableclasses that wire host implementations of those interfaces to aWasmRuntimeviaruntime.BindHostFunction<TDelegate>. One class per WIT package, partial-class-split per resource family.
The bindings layer lifts/lowers between the canonical-ABI wire form
and the generated interface contract. Host methods return
Result<T, ErrorCode> (no throw-on-error idiom) — the bindings
encode both Ok and Err sides faithfully into the canon-ABI retArea.
Installation
dotnet add package WACS.WASI.Preview2
Usage
Recommended: composite host + extension method
using Wacs.Core.Runtime;
using Wacs.WASI.Preview2;
var runtime = new WasmRuntime();
runtime.UseWasiPreview2(b =>
b.WithEnvironment(/* IEnvironment */)
.EnableSockets() // off by default
.EnableHttp()); // off by default
The runtime.UseWasiPreview2(...) extension constructs a
WasiPreview2Host : IBindable that wires all sub-bindings off a
shared ResourceContext in one call. Default posture matches
Wasmtime: random / clocks / cli stdio / FS preopens always wired;
sockets and HTTP require explicit opt-in.
Per-subsystem (verbose)
For embedders who need to override individual subsystems or wire a strict subset, the hand-managed shape is still supported:
using Wacs.Core.Runtime;
using Wacs.WASI.Preview2.Cli;
using Wacs.WASI.Preview2.Clocks;
using Wacs.WASI.Preview2.HostBinding;
using Wacs.WASI.Preview2.Io;
using Wacs.WASI.Preview2.Random;
using Wacs.WASI.Preview2.Sockets;
using Wacs.WASI.Preview2.Http;
using Wacs.ComponentModel.Runtime;
var runtime = new WasmRuntime();
var resources = new ResourceContext();
// Stdio + environment + exit. Default Environment pulls from
// System.Environment; override for sandboxed args/env/cwd.
new CliBindings(resources,
environment: new Environment(),
exit: new ExitHandler(),
stdin: new Stdin(),
stdout: new Stdout(),
stderr: new Stderr())
.BindToRuntime(runtime);
// Clocks
new ClockBindings(resources,
monotonic: new MonotonicClock(),
wall: new WallClock(),
timezone: new Timezone())
.BindToRuntime(runtime);
// I/O streams + poll + error
new IoBindings(resources, poll: new PollSource()).BindToRuntime(runtime);
new StreamBindings(resources).BindToRuntime(runtime);
// Random
new RandomBindings(
random: new Random.Random(),
insecureRandom: new InsecureRandom(),
insecureSeed: new InsecureSeed())
.BindToRuntime(runtime);
// Filesystem (preopens optional)
new FilesystemBindings(resources).BindToRuntime(runtime);
// Sockets (network stub by default; subclass for real I/O)
new SocketsBindings(resources,
instanceNetwork: new InstanceNetworkSource(),
tcpCreate: new TcpCreateSocket(),
udpCreate: new UdpCreateSocket(),
ipNameLookup: new IpNameLookup())
.BindToRuntime(runtime);
// HTTP types (always wire when using outgoing-handler)
new HttpTypes(resources).BindToRuntime(runtime);
new OutgoingHandlerBindings(resources, new OutgoingHandlerSource())
.BindToRuntime(runtime);
Dependency-injection integration
For Microsoft.Extensions.DependencyInjection consumers (ASP.NET,
generic-host worker services, etc.), the
WACS.WASI.Preview2.DependencyInjection companion package
registers every default impl + bindings + a pre-wired Linker
in one call:
using Microsoft.Extensions.DependencyInjection;
using Wacs.WASI.Preview2.DependencyInjection;
var services = new ServiceCollection();
services.AddWasiPreview2(); // every subsystem default registered
// Selective override — DI's normal mechanism. Defaults use TryAdd,
// so any earlier registration wins:
services.AddSingleton<IOutgoingHandler>(_ =>
new HttpClientOutgoingHandler(new HttpClient { Timeout = ... }));
services.AddSingleton<IEnvironment>(_ => new SandboxedEnvironment(...));
using var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
// Resolve a fully pre-bound Linker — every WASI subsystem already
// wired into the runtime, ready for component instantiation.
var linker = scope.ServiceProvider.GetRequiredService<Linker>();
var runtime = linker.Runtime;
AddWasiPreview2(opts => opts.InstanceLifetime = ServiceLifetime.Scoped)
is the default — WasmRuntime / ResourceContext / bindings /
Linker all resolve once per scope (fits ASP.NET request-scoped
wasm execution). Pass Transient for per-call construction or
Singleton for single-component apps.
One-shot run scope: WasiPreview2RuntimeScope
For embedders running a single component against a single
runtime — the typical CLI / tool / one-shot host shape — the DI
package also ships WasiPreview2RuntimeScope, a disposable
that bundles the scope lifecycle into one constructor call:
using Wacs.Core.Runtime;
using Wacs.WASI.Preview2.DependencyInjection;
var runtime = new WasmRuntime();
using var wasi = new WasiPreview2RuntimeScope(
runtime,
preopens: new[] { ("./models", "/models") });
// wasi.Bundle → the resolved bundle (composite when
// Wacs.WASI.NN.DependencyInjection is on the
// load path; base WasiPreview2Bundle otherwise)
// wasi.Resources → the WasiPreview2Resources for ctor arg 3
// wasi.Runtime → runtime, with every *Bindings.BindToRuntime
// already fired
The scope:
- Registers the external
runtimeas a singleton in DI beforeAddWasiPreview2'sTryAdd<WasmRuntime>fires, so the Linker binds against THIS runtime (not a fresh one DI would otherwise build). - Registers a
Preopens(IEnumerable<(host, guest)>)impl asIPreopenswhenpreopensis non-empty, ahead ofAddWasiPreview2'sTryAddSingleton<IPreopens>default. - Auto-detects
Wacs.WASI.NN.DependencyInjectionand registers the compositeWasiPreview2NNBundlewhen available — required because the transpiler emits its direct-link IL against whichever bundle type was visible at transpile time, and a mismatch between transpile-time and run-time bundle types trips anInvalidCastExceptionat the first import call. - Eagerly resolves the
Linkerso the runtime has everywasi:*binding registered by the time the ctor returns.
Dispose releases the underlying IServiceScope and
ServiceProvider. The wasip2 wacs run path uses this; programmatic
embedders should prefer it over hand-rolling the DI scope unless they
need the IServiceCollection for additional registrations (which the
optional configure callback covers).
Validating bindings against the WIT contract
Optional but recommended: catch contract drift at link time before the component runs. See the validation docs for details.
using Wacs.ComponentModel.Validation;
var linker = new Linker(runtime, ValidationLevel.Strict);
linker.Bind(new CliBindings(resources, /* … */));
linker.Bind(new HttpTypes(resources));
linker.Bind(new OutgoingHandlerBindings(resources, handler));
// The WIT files this package was built against are embedded in
// the assembly itself — no need to ship them alongside.
var contract = WitContract.FromAssembly(
typeof(CliBindings).Assembly);
linker.Validate(contract); // throws ValidationException on mismatch
Implementation depth
Every WASI 0.2.3 subsystem ships with a real .NET-backed default implementation. The base classes default to "stub" semantics (empty/no-op) so they don't trap if a host wires them naively; the production-named subclass per subsystem wires through to the real BCL primitive.
| Subsystem | Real-impl class | Backing |
|---|---|---|
wasi:cli (env, exit, stdio) |
Environment / ExitHandler / Stdin / Stdout / Stderr |
System.Environment / Console |
wasi:clocks/monotonic |
MonotonicClock |
Stopwatch (high-res) + Task.Delay for subscribe-* |
wasi:clocks/wall-clock |
WallClock |
DateTimeOffset.UtcNow |
wasi:clocks/timezone |
Timezone |
TimeZoneInfo |
wasi:filesystem |
Descriptor + HostFileInputStream / HostFileOutputStream + Preopens |
System.IO.File / Directory |
wasi:random |
Random / InsecureRandom / InsecureSeed |
RandomNumberGenerator (CSPRNG) + System.Random |
wasi:io/streams |
Through filesystem / cli wrappers | Result-encoded System.IO.Stream |
wasi:io/poll |
PollSource + ManualResetPollable / TimerPollable |
ManualResetEventSlim + Task.WhenAny |
wasi:sockets/tcp |
SystemTcpSocket + SystemTcpCreateSocket |
System.Net.Sockets.Socket (Stream) |
wasi:sockets/udp |
SystemUdpSocket + datagram-stream wrappers |
System.Net.Sockets.Socket (Dgram) |
wasi:sockets/ip-name-lookup |
IpNameLookup + DnsResolveAddressStream |
System.Net.Dns.GetHostAddresses |
wasi:http/outgoing-handler |
HttpClientOutgoingHandler |
System.Net.Http.HttpClient |
Constructors take optional impl arguments — default to the real-
backed class, pass null to skip wiring an interface entirely.
Hosts that need different behavior (sandbox restrictions, custom DNS, fakes for testing) subclass the relevant base type and override the methods they want to replace.
Preopens accepts a list of (hostPath, guestPath) mount pairs
through either of two ctors:
new Preopens(("./models", "/models"),
("./assets", "/assets"))
// or
new Preopens(IEnumerable<(string, string)> mounts)
Each pair becomes a Descriptor wrapping the host path; the guest
sees the list through wasi:filesystem/preopens.get-directories.
Wiring this through the DI scope's IPreopens registration (or
the WasiPreview2RuntimeScope preopens: parameter) lets the
transpiler's direct-link emit lift the
list<tuple<own<descriptor>, string>> return shape inline — no
delegate dispatch on the host call.
License
Apache-2.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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 Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- WACS.ComponentModel (>= 0.4.0)
-
net8.0
- WACS.ComponentModel (>= 0.4.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on WACS.WASI.Preview2:
| Package | Downloads |
|---|---|
|
WACS.WASI.Preview2.DependencyInjection
Microsoft.Extensions.DependencyInjection extension methods for WACS.WASI.Preview2. Registers default host implementations + bindings + a pre-wired Linker. |
|
|
WACS.WASI.GFX
WASI-GFX host bindings for WACS — wasi:graphics-context, wasi:frame-buffer, wasi:surface (all @0.0.1). Backend-agnostic core; the Silk.NET/SDL backend ships as a sibling package. Preview: tracks the wasi-gfx proposal at WASI Phase 2 — the WIT surface may change as the proposal evolves. |
|
|
WACS.WASI.GFX.Webgpu
WASI-GFX `wasi:webgpu@0.0.1` host bindings for WACS — the fourth wasi-gfx WIT package, mirroring the browser WebGPU spec. Contract assembly; backend lives in WACS.WASI.GFX.Silk. Preview: tracks the wasi-gfx proposal at WASI Phase 2 — the WIT surface may change as the proposal evolves. |
GitHub repositories
This package is not used by any popular GitHub repositories.