Skip to content

Instantly share code, notes, and snippets.

@guybedford
Created August 6, 2019 05:23
Show Gist options
  • Save guybedford/890339e83e8cb1abc636a84b003f7a78 to your computer and use it in GitHub Desktop.
Save guybedford/890339e83e8cb1abc636a84b003f7a78 to your computer and use it in GitHub Desktop.

Node.js WASI API Suggestion

This suggestion proposal is in response to the PR at nodejs/node#27850, based on the concerns raised in https://gist.github.com/guybedford/d5f771cf549311af5156c4e6412ef1dd.

Whatever API we ship, should be designed such that we are making minimal locking down assumptions in Node.js core. It is important that Node.js should be able to adapt to new WASI APIs in future, given how unstable the current status is.

To do this, it is important to design the minimal API that can achieve the goals we need for WASI support in Node.js, that makes the least amount of lasting assumptions for the project.

The API provided below is designed to support WASI in a Node.js-compatible way, by making the only API assumption the existence of a new core library which is considered experimental.

By not touching the ES module system at all at this point, we get a lot of the benefits without the deeper integration commitment costs.

Command API

(assuming top-level await support)

The Command API requires absolutely no interception into the ES module loading process, because the model for WASI commands is that they are instantiated

import { execCommand } from 'nodejs_wasi_unstable';

// The benefit of this model is that WASI libraries can run in
// separate threads / processes.
// In addition, internal memoization of instantiation can be done to avoid
// recompilation.
// argv[0] is the path to the .wasm file itself
const commandProcess = await execCommand('./wasi-cmd.wasm', ['args'], {
  env: process.env,
  preopen: { ... }
});

// Start event to get synchronous execution start.
commandProcess.on('start')

// Streams are available for read/write while command is "alive"
commandProcess.stdin.write('Hello');
commandProcess.stdout.on('chunk', function (data) {
});
commandProcess.stderr;

// Same events available as child processes.
commandProcess.on('close', function () {
  console.log('Done');
});

// Basically API should be roughly identical.
commandProcess.kill();

// Creates another instance of the same command.
const commandProcess2 = await execCommand('./wasi-cmd.wasm');

Reactor API

The Reactor API intercepts the ES module system loading process, because these reactors are libraries that should be supported alongside ES modules.

This could be done based on detecting the wasi_unstable import / __wasi_unstable_reactor_start export.

The Reactor API is still pending in WebAssembly/wasi-libc#74.

// Automatically runs `__wasi_unstable_reactor_start` and correctly links the WASI binary
import { method } from './wasi-reactor.wasm';

There are still a number of things under discussion in this model though, for example there might even be a dedicated WASM header or custom section information indicating to use the WASI loading semantics. This has been proposed by some in the Web Assembly committee.

As such, and since there aren't many examples of even Reactor modules yet, it could be worth waiting on this API variant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment