wasd 0.4.0 copy "wasd: ^0.4.0" to clipboard
wasd: ^0.4.0 copied to clipboard

A pure Dart WebAssembly runtime for Dart and Flutter, with WASI Preview1 and Dart VM runners for stable WASI 0.2.12 command and HTTP proxy components.

WASD #

pub package Dart SDK License GitHub Stars

A pure Dart WebAssembly runtime for Dart and Flutter ecosystems.

WASD provides Dart-native WebAssembly execution with a pure Dart core runtime layer, so you can embed and run Wasm modules directly from Dart code without relying on a native runtime dependency in the core library.

Overview #

WASD is a Dart package for:

  • Decoding and validating WebAssembly binaries
  • Compiling and instantiating modules from bytes or streams
  • Instantiating modules with host imports
  • Executing exported functions from Dart
  • Running WASI Preview1 command modules
  • Running stable WASI 0.2.12 command and HTTP proxy components on Dart VM
  • Inspecting module imports/exports/custom sections

Why WASD #

  • Pure Dart core runtime, aligned with Dart/Flutter embedding workflows
  • Public API that mirrors WebAssembly-style operations (compile, instantiate, validate)
  • Explicit host integration via import maps and typed wrappers
  • Built-in WASI Preview1 host plus Preview2 command/proxy runners through WASI
  • Regression-oriented tests and conformance tooling in-repo

Installation #

dart pub add wasd

Or add manually in pubspec.yaml:

dependencies:
  wasd: ^0.4.0

Quick Start #

Run included examples:

dart run example/wasm_cli.dart
dart run example/wasm_cli.dart 3 9

The Flutter DOOM example has its own guide in the GitHub repository.

Minimal module invocation:

import 'dart:typed_data';
import 'package:wasd/wasd.dart';

Future<void> main() async {
  final Uint8List wasmBytes = loadYourModuleBytes();
  final runtime = await WebAssembly.instantiate(wasmBytes.buffer);
  final addExport = runtime.instance.exports['add'];
  if (addExport is! FunctionImportExportValue) {
    throw StateError('Expected `add` export to be a function.');
  }

  final result = (addExport.ref([20, 22]) as num).toInt();
  print(result); // 42
}

Uint8List loadYourModuleBytes() => throw UnimplementedError();

Host Function Imports #

Provide host callbacks with Imports and ImportExportKind.function:

import 'dart:typed_data';
import 'package:wasd/wasd.dart';

Future<void> main() async {
  final wasmBytes = loadYourModuleBytes();
  final imports = <String, ModuleImports>{
    'env': {
      'plus': ImportExportKind.function((args) {
        final a = args[0] as int;
        final b = args[1] as int;
        return a + b;
      }),
    },
  };

  final runtime = await WebAssembly.instantiate(wasmBytes.buffer, imports);
  final usePlus = runtime.instance.exports['use_plus'];
  if (usePlus is! FunctionImportExportValue) {
    throw StateError('Expected `use_plus` export to be a function.');
  }

  print(usePlus.ref([4, 5])); // 9
}

Uint8List loadYourModuleBytes() => throw UnimplementedError();

WASI Preview1 #

Use WASI and call _start through wasi.start(instance).

import 'package:wasd/wasd.dart';

Future<void> main() async {
  final wasmBytes = loadWasiModuleBytes();
  final wasi = WASI(
    args: const ['demo'],
    env: const {'FOO': 'bar'},
  );

  final runtime = await WebAssembly.instantiate(wasmBytes.buffer, wasi.imports);
  final exitCode = wasi.start(runtime.instance);

  print('exitCode=$exitCode');
}

Uint8List loadWasiModuleBytes() => throw UnimplementedError();

To capture guest output instead of forwarding it to the host process streams, provide per-instance byte sinks. Sinks receive raw bytes synchronously, so the host chooses whether to buffer, stream, or limit the output.

import 'dart:typed_data';
import 'package:wasd/wasd.dart';

final stdout = BytesBuilder();
final stderr = BytesBuilder();
final wasi = WASI(stdoutSink: stdout.add, stderrSink: stderr.add);

WASI Preview2 (Dart VM) #

Decode a stable WASI 0.2.12 wasi:cli/command component and run it with the native Preview2 host:

import 'dart:io';
import 'package:wasd/wasd.dart';

Future<void> main() async {
  final bytes = await File('app.component.wasm').readAsBytes();
  final component = WasmComponent.decode(bytes);
  final host = WASI.preview2(args: const ['app.component.wasm']);
  final result = await WASIPreview2CommandRunner(host).run(component);

  print('exitCode=${result.exitCode}');
}

WASIPreview2ProxyRunner executes stable wasi:http/proxy incoming handlers against a WASIPreview2HttpIncomingRequest. Preview2 execution is currently native Dart VM only and targets the synchronous Canonical ABI required by these stable WASI 0.2.12 worlds.

final proxyComponent = WasmComponent.decode(
  await File('proxy.component.wasm').readAsBytes(),
);
final proxyHost = WASI.preview2();
final request = WASIPreview2HttpIncomingRequest(
  method: const WASIPreview2HttpMethod.standard('get'),
  headers: WASIPreview2HttpFields(),
  pathWithQuery: '/',
  scheme: const WASIPreview2HttpScheme.standard('HTTP'),
  authority: 'example.test',
);
final response = await WASIPreview2ProxyRunner(proxyHost).handle(
  proxyComponent,
  request,
);

Module Metadata #

import 'dart:typed_data';
import 'package:wasd/wasd.dart';

Future<void> main() async {
  final wasmBytes = loadYourModuleBytes();
  final module = await WebAssembly.compile(wasmBytes.buffer);
  final imports = Module.imports(module);
  final exports = Module.exports(module);

  print('imports=${imports.length} exports=${exports.length}');
}

Uint8List loadYourModuleBytes() => throw UnimplementedError();

Verification #

dart analyze
dart test test/wasi_test.dart test/wasm_test.dart
dart test test/wasi_preview2_conformance_test.dart test/wasi_preview2_http_proxy_toolchain_test.dart

Compatibility Snapshot #

WebAssembly Implementation Version #

Item Version Status
Core Wasm module binary 0x01 0x00 0x00 0x00 Supported

WASI Version #

WASI Version Status
Preview 1 Supported for wasi_snapshot_preview1 command modules
Preview 2 Native Dart VM execution for stable WASI 0.2.12 wasi:cli/command and wasi:http/proxy components, with the required random, clocks, io, cli, filesystem, sockets, and http host import bindings
Preview 3 Not supported for execution; experimental host/filesystem/async scaffolding remains available

Runtime Support #

Runtime Preview1 host Preview2 runner Filesystem model
Dart VM In-repo wasi_snapshot_preview1; passes official wasm32-wasip1 wasi-testsuite command modules Stable WASI 0.2.12 command and HTTP proxy components Real host preopens plus portable in-memory VFS
Node.js In-repo wasi_snapshot_preview1, not node:wasi Not yet supported Real host preopens plus portable in-memory VFS
Browser JS In-repo wasi_snapshot_preview1 Not yet supported Portable in-memory VFS

The Preview2 runner deliberately does not claim general Component Model coverage. Component Model 0.3 async/future/stream/task features, experimental proposal packages, and full Preview3 execution remain out of scope.

When dart:io cannot faithfully implement a Preview2 socket operation, the native adapter returns not-supported instead of reporting simulated success. Native TCP bind/listen is currently unsupported; native TCP connect and UDP bind/connect remain available.

Dart HttpClient does not expose HTTP trailers. Native outgoing-handler requests with trailers and incoming responses that declare trailers therefore report HTTP-protocol-error; proxy response trailers remain available to the host caller.

Native outgoing HTTP preserves encoded response bodies and redirect responses; it does not transparently decompress content or follow redirects.

The socket resolver performs dependency-free IDNA ToASCII conversion for a conservative canonical Unicode subset and validates existing A-labels. Disallowed symbols, malformed A-labels, and labels that require Unicode normalization tables or ContextJ/ContextO processing are rejected with invalid-argument instead of producing a non-canonical DNS name.

Contributing #

Contributions are welcome through pull requests and issues.

  • Follow existing lint/style rules (dart format ., dart analyze)
  • Add focused regression tests for behavior changes
  • Keep changes scoped and reproducible with command output

License #

WASD is licensed under the MIT License. See LICENSE.

6
likes
150
points
256
downloads

Documentation

API reference

Publisher

verified publishermedz.dev

Weekly Downloads

A pure Dart WebAssembly runtime for Dart and Flutter, with WASI Preview1 and Dart VM runners for stable WASI 0.2.12 command and HTTP proxy components.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

ffi

More

Packages that depend on wasd