diff --git a/README.md b/README.md
index 263b0bc..9c5ab25 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@ Plugins are sandboxed by default - they cannot access the filesystem or network
Plugins like `ls` or `cat` can interact with the filesystem using the primitives of the languages they are written in.
- on the CLI, a folder from the disk is mounted via the `--dir` flag
-- on the browser, a virtual filesystem is mounted, the I/O operations are forwarded via the `@bytecodealliance/preview2-shim/filesystem` shim, which shims the `wasi:filesystem` filesystem interface
+- on the browser, a virtual filesystem is mounted, the I/O operations are forwarded via a [local fork](./packages/web-host/overrides/@bytecodealliance/preview2-shim) of `@bytecodealliance/preview2-shim/filesystem` shim, which shims the `wasi:filesystem` filesystem interface

@@ -410,6 +410,13 @@ pluginlab\
- formating the rust code
- typechecking the TypeScript code
+### Local fork of `@bytecodealliance/preview2-shim`
+
+- The original [`@bytecodealliance/preview2-shim`](https://github.com/bytecodealliance/jco/tree/main/packages/preview2-shim) for the **browser** doesn't support **WRITE** operations on the filesystem
+- A fork was created in [`packages/web-host/overrides/@bytecodealliance/preview2-shim`](./packages/web-host/overrides/@bytecodealliance/preview2-shim) to fix this issue
+
+Everything is described in [PR#15 Support plugin-tee in the web host](https://github.com/topheman/webassembly-component-model-experiments/pull/15) (must read 😉).
+
## Resources
### Optional tools
diff --git a/biome.json b/biome.json
index f016099..9969565 100644
--- a/biome.json
+++ b/biome.json
@@ -6,7 +6,8 @@
"!**/node_modules",
"!**/dist",
"!**/build",
- "!**/package.json"
+ "!**/package.json",
+ "!packages/web-host/overrides/**"
]
},
"formatter": {
diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs
index 2a5e379..8447926 100644
--- a/lint-staged.config.mjs
+++ b/lint-staged.config.mjs
@@ -1,6 +1,6 @@
export default {
- 'packages/**': 'biome check --write',
+ 'packages/*/!(overrides)/**': 'biome check --write',
'packages/plugin-echo/**/*.ts': () => 'npm run typecheck --workspace=packages/plugin-echo',
'packages/web-host/**/*.{ts,tsx}': () => 'npm run typecheck --workspace=packages/web-host',
// `cargo fmt doesn't accept files
diff --git a/packages/web-host/clis/prepareFilesystem.ts b/packages/web-host/clis/prepareFilesystem.ts
index 7d1a3fc..ab748da 100755
--- a/packages/web-host/clis/prepareFilesystem.ts
+++ b/packages/web-host/clis/prepareFilesystem.ts
@@ -53,6 +53,18 @@ function assertPathIsString(path: string): asserts path is string {
}
}
+function template(data: VirtualFs): string {
+ return `// THIS FILE IS GENERATED BY THE prepareVirtualFs COMMAND, DO NOT EDIT IT MANUALLY
+
+// It is meant to be used for mounting a virtual filesystem in the browser
+// interacting with @bytecodealliance/preview2-shim/filesystem , the shim for wasi:filesystem
+//
+// The \`fs\` calls like \`read\`, \`readDir\` ... in rust or other languages will be redirected to this virtual filesystem
+// and will interact as if the filesystem was a real one.
+
+export function makeVirtualFs() { return ${JSON.stringify(data, null, 2)}; }`;
+}
+
function run() {
program
.description("Prepare wasm files for the web host")
@@ -65,17 +77,7 @@ function run() {
if (options.format === "json") {
console.log(JSON.stringify(virtualFs, null, 2));
} else if (options.format === "ts") {
- console.log(
- `// THIS FILE IS GENERATED BY THE prepareVirtualFs COMMAND, DO NOT EDIT IT MANUALLY
-
-// It is meant to be used for mounting a virtual filesystem in the browser
-// interacting with @bytecodealliance/preview2-shim/filesystem , the shim for wasi:filesystem
-//
-// The \`fs\` calls like \`read\`, \`readDir\` ... in rust or other languages will be redirected to this virtual filesystem
-// and will interact as if the filesystem was a real one.
-
-export function makeVirtualFs() { return ${JSON.stringify(virtualFs, null, 2)}; }`,
- );
+ console.log(template(virtualFs));
}
});
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/README.md b/packages/web-host/overrides/@bytecodealliance/preview2-shim/README.md
new file mode 100644
index 0000000..6a1b6f8
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/README.md
@@ -0,0 +1,18 @@
+# Preview2 Shim
+
+WASI Preview2 implementations for Node.js & browsers.
+
+Node.js support is fully tested and conformant against the Wasmtime test suite.
+
+Browser support is considered experimental, and not currently suitable for production applications.
+
+# License
+
+This project is licensed under the Apache 2.0 license with the LLVM exception.
+See [LICENSE](LICENSE) for more details.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this project by you, as defined in the Apache-2.0 license,
+shall be licensed as above, without any additional terms or conditions.
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/cli.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/cli.js
new file mode 100644
index 0000000..a265411
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/cli.js
@@ -0,0 +1,162 @@
+import { _setCwd as fsSetCwd } from './filesystem.js';
+import { streams } from './io.js';
+const { InputStream, OutputStream } = streams;
+
+const symbolDispose = Symbol.dispose ?? Symbol.for('dispose');
+
+let _env = [], _args = [], _cwd = "/";
+export function _setEnv (envObj) {
+ _env = Object.entries(envObj);
+}
+export function _setArgs (args) {
+ _args = args;
+}
+
+export function _setCwd (cwd) {
+ fsSetCwd(_cwd = cwd);
+}
+
+export const environment = {
+ getEnvironment () {
+ return _env;
+ },
+ getArguments () {
+ return _args;
+ },
+ initialCwd () {
+ return _cwd;
+ }
+};
+
+class ComponentExit extends Error {
+ constructor(code) {
+ super(`Component exited ${code === 0 ? 'successfully' : 'with error'}`);
+ this.exitError = true;
+ this.code = code;
+ }
+}
+
+export const exit = {
+ exit (status) {
+ throw new ComponentExit(status.tag === 'err' ? 1 : 0);
+ },
+ exitWithCode (code) {
+ throw new ComponentExit(code);
+ }
+};
+
+/**
+ * @param {import('../common/io.js').InputStreamHandler} handler
+ */
+export function _setStdin (handler) {
+ stdinStream.handler = handler;
+}
+/**
+ * @param {import('../common/io.js').OutputStreamHandler} handler
+ */
+export function _setStderr (handler) {
+ stderrStream.handler = handler;
+}
+/**
+ * @param {import('../common/io.js').OutputStreamHandler} handler
+ */
+export function _setStdout (handler) {
+ stdoutStream.handler = handler;
+}
+
+const stdinStream = new InputStream({
+ blockingRead (_len) {
+ // TODO
+ },
+ subscribe () {
+ // TODO
+ },
+ [symbolDispose] () {
+ // TODO
+ }
+});
+let textDecoder = new TextDecoder();
+const stdoutStream = new OutputStream({
+ write (contents) {
+ if (contents[contents.length - 1] == 10) {
+ // console.log already appends a new line
+ contents = contents.subarray(0, contents.length - 1);
+ }
+ console.log(textDecoder.decode(contents));
+ },
+ blockingFlush () {
+ },
+ [symbolDispose] () {
+ }
+});
+const stderrStream = new OutputStream({
+ write (contents) {
+ if (contents[contents.length - 1] == 10) {
+ // console.error already appends a new line
+ contents = contents.subarray(0, contents.length - 1);
+ }
+ console.error(textDecoder.decode(contents));
+ },
+ blockingFlush () {
+ },
+ [symbolDispose] () {
+
+ }
+});
+
+export const stdin = {
+ InputStream,
+ getStdin () {
+ return stdinStream;
+ }
+};
+
+export const stdout = {
+ OutputStream,
+ getStdout () {
+ return stdoutStream;
+ }
+};
+
+export const stderr = {
+ OutputStream,
+ getStderr () {
+ return stderrStream;
+ }
+};
+
+class TerminalInput {}
+class TerminalOutput {}
+
+const terminalStdoutInstance = new TerminalOutput();
+const terminalStderrInstance = new TerminalOutput();
+const terminalStdinInstance = new TerminalInput();
+
+export const terminalInput = {
+ TerminalInput
+};
+
+export const terminalOutput = {
+ TerminalOutput
+};
+
+export const terminalStderr = {
+ TerminalOutput,
+ getTerminalStderr () {
+ return terminalStderrInstance;
+ }
+};
+
+export const terminalStdin = {
+ TerminalInput,
+ getTerminalStdin () {
+ return terminalStdinInstance;
+ }
+};
+
+export const terminalStdout = {
+ TerminalOutput,
+ getTerminalStdout () {
+ return terminalStdoutInstance;
+ }
+};
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/clocks.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/clocks.js
new file mode 100644
index 0000000..0a0b7bd
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/clocks.js
@@ -0,0 +1,34 @@
+export const monotonicClock = {
+ resolution() {
+ // usually we dont get sub-millisecond accuracy in the browser
+ // Note: is there a better way to determine this?
+ return 1e6;
+ },
+ now () {
+ // performance.now() is in milliseconds, but we want nanoseconds
+ return BigInt(Math.floor(performance.now() * 1e6));
+ },
+ subscribeInstant (instant) {
+ instant = BigInt(instant);
+ const now = this.now();
+ if (instant <= now)
+ return this.subscribeDuration(0);
+ return this.subscribeDuration(instant - now);
+ },
+ subscribeDuration (_duration) {
+ _duration = BigInt(_duration);
+ console.log(`[monotonic-clock] subscribe`);
+ }
+};
+
+export const wallClock = {
+ now() {
+ let now = Date.now(); // in milliseconds
+ const seconds = BigInt(Math.floor(now / 1e3));
+ const nanoseconds = (now % 1e3) * 1e6;
+ return { seconds, nanoseconds };
+ },
+ resolution() {
+ return { seconds: 0n, nanoseconds: 1e6 };
+ }
+};
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/filesystem.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/filesystem.js
new file mode 100644
index 0000000..16784d2
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/filesystem.js
@@ -0,0 +1,295 @@
+import { streams } from './io.js';
+import { environment } from './cli.js';
+
+const { InputStream, OutputStream } = streams;
+
+let _cwd = "/";
+
+export function _setCwd (cwd) {
+ _cwd = cwd;
+}
+
+export function _setFileData (fileData) {
+ _fileData = fileData;
+ _rootPreopen[0] = new Descriptor(fileData);
+ const cwd = environment.initialCwd();
+ _setCwd(cwd || '/');
+}
+
+export function _getFileData () {
+ return JSON.stringify(_fileData);
+}
+
+let _fileData = { dir: {} };
+
+const timeZero = {
+ seconds: BigInt(0),
+ nanoseconds: 0
+};
+
+function getChildEntry (parentEntry, subpath, openFlags) {
+ if (subpath === '.' && _rootPreopen && descriptorGetEntry(_rootPreopen[0]) === parentEntry) {
+ subpath = _cwd;
+ if (subpath.startsWith('/') && subpath !== '/')
+ subpath = subpath.slice(1);
+ }
+ let entry = parentEntry;
+ let segmentIdx;
+ do {
+ if (!entry || !entry.dir) throw 'not-directory';
+ segmentIdx = subpath.indexOf('/');
+ const segment = segmentIdx === -1 ? subpath : subpath.slice(0, segmentIdx);
+ if (segment === '..') throw 'no-entry';
+ if (segment === '.' || segment === '');
+ else if (!entry.dir[segment] && openFlags.create)
+ entry = entry.dir[segment] = openFlags.directory ? { dir: {} } : { source: new Uint8Array([]) };
+ else
+ entry = entry.dir[segment];
+ subpath = subpath.slice(segmentIdx + 1);
+ } while (segmentIdx !== -1)
+ if (!entry) throw 'no-entry';
+ return entry;
+}
+
+function getSource (fileEntry) {
+ if (typeof fileEntry.source === 'string') {
+ fileEntry.source = new TextEncoder().encode(fileEntry.source);
+ }
+ return fileEntry.source;
+}
+
+class DirectoryEntryStream {
+ constructor (entries) {
+ this.idx = 0;
+ this.entries = entries;
+ }
+ readDirectoryEntry () {
+ if (this.idx === this.entries.length)
+ return null;
+ const [name, entry] = this.entries[this.idx];
+ this.idx += 1;
+ return {
+ name,
+ type: entry.dir ? 'directory' : 'regular-file'
+ };
+ }
+}
+
+class Descriptor {
+ #stream;
+ #entry;
+ #mtime = 0;
+
+ _getEntry (descriptor) {
+ return descriptor.#entry;
+ }
+
+ constructor (entry, isStream) {
+ if (isStream)
+ this.#stream = entry;
+ else
+ this.#entry = entry;
+ }
+
+ readViaStream(_offset) {
+ const source = getSource(this.#entry);
+ let offset = Number(_offset);
+ return new InputStream({
+ blockingRead (len) {
+ if (offset === source.byteLength)
+ throw { tag: 'closed' };
+ const bytes = source.slice(offset, offset + Number(len));
+ offset += bytes.byteLength;
+ return bytes;
+ }
+ });
+ }
+
+ writeViaStream(_offset) {
+ const entry = this.#entry;
+ let offset = Number(_offset);
+ return new OutputStream({
+ write (buf) {
+ const newSource = new Uint8Array(buf.byteLength);
+ newSource.set(buf, 0);
+ offset += buf.byteLength;
+ entry.source = newSource;
+ return buf.byteLength;
+ }
+ });
+ }
+
+ appendViaStream() {
+ const entry = this.#entry;
+ return new OutputStream({
+ write (buf) {
+ const entrySource = getSource(entry);
+ const newSource = new Uint8Array(buf.byteLength + entrySource.byteLength);
+ newSource.set(entrySource, 0);
+ newSource.set(buf, entrySource.byteLength);
+ entry.source = newSource;
+ return buf.byteLength;
+ }
+ });
+ }
+
+ advise(descriptor, offset, length, advice) {
+ console.log(`[filesystem] ADVISE`, descriptor, offset, length, advice);
+ }
+
+ syncData() {
+ console.log(`[filesystem] SYNC DATA`);
+ }
+
+ getFlags() {
+ console.log(`[filesystem] FLAGS FOR`);
+ }
+
+ getType() {
+ if (this.#stream) return 'fifo';
+ if (this.#entry.dir) return 'directory';
+ if (this.#entry.source) return 'regular-file';
+ return 'unknown';
+ }
+
+ setSize(size) {
+ console.log(`[filesystem] SET SIZE`, size);
+ }
+
+ setTimes(dataAccessTimestamp, dataModificationTimestamp) {
+ console.log(`[filesystem] SET TIMES`, dataAccessTimestamp, dataModificationTimestamp);
+ }
+
+ read(length, offset) {
+ const source = getSource(this.#entry);
+ return [source.slice(offset, offset + length), offset + length >= source.byteLength];
+ }
+
+ write(buffer, offset) {
+ if (offset !== 0) throw 'invalid-seek';
+ this.#entry.source = buffer;
+ return buffer.byteLength;
+ }
+
+ readDirectory() {
+ if (!this.#entry?.dir)
+ throw 'bad-descriptor';
+ return new DirectoryEntryStream(Object.entries(this.#entry.dir).sort(([a], [b]) => a > b ? 1 : -1));
+ }
+
+ sync() {
+ console.log(`[filesystem] SYNC`);
+ }
+
+ createDirectoryAt(path) {
+ const entry = getChildEntry(this.#entry, path, { create: true, directory: true });
+ if (entry.source) throw 'exist';
+ }
+
+ stat() {
+ let type = 'unknown', size = BigInt(0);
+ if (this.#entry.source) {
+ type = 'regular-file';
+ const source = getSource(this.#entry);
+ size = BigInt(source.byteLength);
+ }
+ else if (this.#entry.dir) {
+ type = 'directory';
+ }
+ return {
+ type,
+ linkCount: BigInt(0),
+ size,
+ dataAccessTimestamp: timeZero,
+ dataModificationTimestamp: timeZero,
+ statusChangeTimestamp: timeZero,
+ }
+ }
+
+ statAt(_pathFlags, path) {
+ const entry = getChildEntry(this.#entry, path, { create: false, directory: false });
+ let type = 'unknown', size = BigInt(0);
+ if (entry.source) {
+ type = 'regular-file';
+ const source = getSource(entry);
+ size = BigInt(source.byteLength);
+ }
+ else if (entry.dir) {
+ type = 'directory';
+ }
+ return {
+ type,
+ linkCount: BigInt(0),
+ size,
+ dataAccessTimestamp: timeZero,
+ dataModificationTimestamp: timeZero,
+ statusChangeTimestamp: timeZero,
+ };
+ }
+
+ setTimesAt() {
+ console.log(`[filesystem] SET TIMES AT`);
+ }
+
+ linkAt() {
+ console.log(`[filesystem] LINK AT`);
+ }
+
+ openAt(_pathFlags, path, openFlags, _descriptorFlags, _modes) {
+ const childEntry = getChildEntry(this.#entry, path, openFlags);
+ return new Descriptor(childEntry);
+ }
+
+ readlinkAt() {
+ console.log(`[filesystem] READLINK AT`);
+ }
+
+ removeDirectoryAt() {
+ console.log(`[filesystem] REMOVE DIR AT`);
+ }
+
+ renameAt() {
+ console.log(`[filesystem] RENAME AT`);
+ }
+
+ symlinkAt() {
+ console.log(`[filesystem] SYMLINK AT`);
+ }
+
+ unlinkFileAt() {
+ console.log(`[filesystem] UNLINK FILE AT`);
+ }
+
+ isSameObject(other) {
+ return other === this;
+ }
+
+ metadataHash() {
+ let upper = BigInt(0);
+ upper += BigInt(this.#mtime);
+ return { upper, lower: BigInt(0) };
+ }
+
+ metadataHashAt(_pathFlags, _path) {
+ let upper = BigInt(0);
+ upper += BigInt(this.#mtime);
+ return { upper, lower: BigInt(0) };
+ }
+}
+const descriptorGetEntry = Descriptor.prototype._getEntry;
+delete Descriptor.prototype._getEntry;
+
+let _preopens = [[new Descriptor(_fileData), '/']], _rootPreopen = _preopens[0];
+
+export const preopens = {
+ getDirectories () {
+ return _preopens;
+ }
+}
+
+export const types = {
+ Descriptor,
+ DirectoryEntryStream
+};
+
+export { types as filesystemTypes }
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/http.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/http.js
new file mode 100644
index 0000000..4a1b244
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/http.js
@@ -0,0 +1,144 @@
+/**
+ * @param {import("../../types/interfaces/wasi-http-types").Request} req
+ * @returns {string}
+ */
+export function send(req) {
+ console.log(`[http] Send (browser) ${req.uri}`);
+ try {
+ const xhr = new XMLHttpRequest();
+ xhr.open(req.method.toString(), req.uri, false);
+ const requestHeaders = new Headers(req.headers);
+ for (let [name, value] of requestHeaders.entries()) {
+ if (name !== "user-agent" && name !== "host") {
+ xhr.setRequestHeader(name, value);
+ }
+ }
+ xhr.send(req.body && req.body.length > 0 ? req.body : null);
+ const body = xhr.response ? new TextEncoder().encode(xhr.response) : undefined;
+ const headers = [];
+ xhr.getAllResponseHeaders().trim().split(/[\r\n]+/).forEach((line) => {
+ var parts = line.split(': ');
+ var key = parts.shift();
+ var value = parts.join(': ');
+ headers.push([key, value]);
+ });
+ return {
+ status: xhr.status,
+ headers,
+ body,
+ };
+ } catch (err) {
+ throw new Error(err.message);
+ }
+}
+
+export const incomingHandler = {
+ handle () {
+
+ }
+};
+
+export const outgoingHandler = {
+ handle () {
+
+ }
+};
+
+export const types = {
+ dropFields(_fields) {
+ console.log("[types] Drop fields");
+ },
+ newFields(_entries) {
+ console.log("[types] New fields");
+ },
+ fieldsGet(_fields, _name) {
+ console.log("[types] Fields get");
+ },
+ fieldsSet(_fields, _name, _value) {
+ console.log("[types] Fields set");
+ },
+ fieldsDelete(_fields, _name) {
+ console.log("[types] Fields delete");
+ },
+ fieldsAppend(_fields, _name, _value) {
+ console.log("[types] Fields append");
+ },
+ fieldsEntries(_fields) {
+ console.log("[types] Fields entries");
+ },
+ fieldsClone(_fields) {
+ console.log("[types] Fields clone");
+ },
+ finishIncomingStream(s) {
+ console.log(`[types] Finish incoming stream ${s}`);
+ },
+ finishOutgoingStream(s, _trailers) {
+ console.log(`[types] Finish outgoing stream ${s}`);
+ },
+ dropIncomingRequest(_req) {
+ console.log("[types] Drop incoming request");
+ },
+ dropOutgoingRequest(_req) {
+ console.log("[types] Drop outgoing request");
+ },
+ incomingRequestMethod(_req) {
+ console.log("[types] Incoming request method");
+ },
+ incomingRequestPathWithQuery(_req) {
+ console.log("[types] Incoming request path with query");
+ },
+ incomingRequestScheme(_req) {
+ console.log("[types] Incoming request scheme");
+ },
+ incomingRequestAuthority(_req) {
+ console.log("[types] Incoming request authority");
+ },
+ incomingRequestHeaders(_req) {
+ console.log("[types] Incoming request headers");
+ },
+ incomingRequestConsume(_req) {
+ console.log("[types] Incoming request consume");
+ },
+ newOutgoingRequest(_method, _pathWithQuery, _scheme, _authority, _headers) {
+ console.log("[types] New outgoing request");
+ },
+ outgoingRequestWrite(_req) {
+ console.log("[types] Outgoing request write");
+ },
+ dropResponseOutparam(_res) {
+ console.log("[types] Drop response outparam");
+ },
+ setResponseOutparam(_response) {
+ console.log("[types] Drop fields");
+ },
+ dropIncomingResponse(_res) {
+ console.log("[types] Drop incoming response");
+ },
+ dropOutgoingResponse(_res) {
+ console.log("[types] Drop outgoing response");
+ },
+ incomingResponseStatus(_res) {
+ console.log("[types] Incoming response status");
+ },
+ incomingResponseHeaders(_res) {
+ console.log("[types] Incoming response headers");
+ },
+ incomingResponseConsume(_res) {
+ console.log("[types] Incoming response consume");
+ },
+ newOutgoingResponse(_statusCode, _headers) {
+ console.log("[types] New outgoing response");
+ },
+ outgoingResponseWrite(_res) {
+ console.log("[types] Outgoing response write");
+ },
+ dropFutureIncomingResponse(_f) {
+ console.log("[types] Drop future incoming response");
+ },
+ futureIncomingResponseGet(_f) {
+ console.log("[types] Future incoming response get");
+ },
+ listenToFutureIncomingResponse(_f) {
+ console.log("[types] Listen to future incoming response");
+ }
+};
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/index.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/index.js
new file mode 100644
index 0000000..da23919
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/index.js
@@ -0,0 +1,17 @@
+import * as clocks from "./clocks.js";
+import * as filesystem from "./filesystem.js";
+import * as http from "./http.js";
+import * as io from "./io.js";
+import * as random from "./random.js";
+import * as sockets from "./sockets.js";
+import * as cli from "./cli.js";
+
+export {
+ clocks,
+ filesystem,
+ http,
+ io,
+ random,
+ sockets,
+ cli,
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/io.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/io.js
new file mode 100644
index 0000000..b450678
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/io.js
@@ -0,0 +1,185 @@
+let id = 0;
+
+const symbolDispose = Symbol.dispose || Symbol.for('dispose');
+
+const IoError = class Error {
+ constructor (msg) {
+ this.msg = msg;
+ }
+ toDebugString () {
+ return this.msg;
+ }
+}
+
+/**
+ * @typedef {{
+ * read?: (len: BigInt) => Uint8Array,
+ * blockingRead: (len: BigInt) => Uint8Array,
+ * skip?: (len: BigInt) => BigInt,
+ * blockingSkip?: (len: BigInt) => BigInt,
+ * subscribe: () => void,
+ * drop?: () => void,
+ * }} InputStreamHandler
+ *
+ * @typedef {{
+ * checkWrite?: () -> BigInt,
+ * write: (buf: Uint8Array) => BigInt,
+ * blockingWriteAndFlush?: (buf: Uint8Array) => void,
+ * flush?: () => void,
+ * blockingFlush: () => void,
+ * writeZeroes?: (len: BigInt) => void,
+ * blockingWriteZeroes?: (len: BigInt) => void,
+ * blockingWriteZeroesAndFlush?: (len: BigInt) => void,
+ * splice?: (src: InputStream, len: BigInt) => BigInt,
+ * blockingSplice?: (src: InputStream, len: BigInt) => BigInt,
+ * forward?: (src: InputStream) => void,
+ * subscribe?: () => void,
+ * drop?: () => void,
+ * }} OutputStreamHandler
+ *
+ **/
+
+class InputStream {
+ /**
+ * @param {InputStreamHandler} handler
+ */
+ constructor (handler) {
+ if (!handler)
+ console.trace('no handler');
+ this.id = ++id;
+ this.handler = handler;
+ }
+ read(len) {
+ if (this.handler.read)
+ return this.handler.read(len);
+ return this.handler.blockingRead.call(this, len);
+ }
+ blockingRead(len) {
+ return this.handler.blockingRead.call(this, len);
+ }
+ skip(len) {
+ if (this.handler.skip)
+ return this.handler.skip.call(this, len);
+ if (this.handler.read) {
+ const bytes = this.handler.read.call(this, len);
+ return BigInt(bytes.byteLength);
+ }
+ return this.blockingSkip.call(this, len);
+ }
+ blockingSkip(len) {
+ if (this.handler.blockingSkip)
+ return this.handler.blockingSkip.call(this, len);
+ const bytes = this.handler.blockingRead.call(this, len);
+ return BigInt(bytes.byteLength);
+ }
+ subscribe() {
+ console.log(`[streams] Subscribe to input stream ${this.id}`);
+ }
+ [symbolDispose] () {
+ if (this.handler.drop)
+ this.handler.drop.call(this);
+ }
+}
+
+class OutputStream {
+ /**
+ * @param {OutputStreamHandler} handler
+ */
+ constructor (handler) {
+ if (!handler)
+ console.trace('no handler');
+ this.id = ++id;
+ this.open = true;
+ this.handler = handler;
+ }
+ checkWrite(len) {
+ if (!this.open)
+ return 0n;
+ if (this.handler.checkWrite)
+ return this.handler.checkWrite.call(this, len);
+ return 1_000_000n;
+ }
+ write(buf) {
+ this.handler.write.call(this, buf);
+ }
+ blockingWriteAndFlush(buf) {
+ /// Perform a write of up to 4096 bytes, and then flush the stream. Block
+ /// until all of these operations are complete, or an error occurs.
+ ///
+ /// This is a convenience wrapper around the use of `check-write`,
+ /// `subscribe`, `write`, and `flush`, and is implemented with the
+ /// following pseudo-code:
+ ///
+ /// ```text
+ /// let pollable = this.subscribe();
+ /// while !contents.is_empty() {
+ /// // Wait for the stream to become writable
+ /// poll-one(pollable);
+ /// let Ok(n) = this.check-write(); // eliding error handling
+ /// let len = min(n, contents.len());
+ /// let (chunk, rest) = contents.split_at(len);
+ /// this.write(chunk ); // eliding error handling
+ /// contents = rest;
+ /// }
+ /// this.flush();
+ /// // Wait for completion of `flush`
+ /// poll-one(pollable);
+ /// // Check for any errors that arose during `flush`
+ /// let _ = this.check-write(); // eliding error handling
+ /// ```
+ this.handler.write.call(this, buf);
+ }
+ flush() {
+ if (this.handler.flush)
+ this.handler.flush.call(this);
+ }
+ blockingFlush() {
+ this.open = true;
+ }
+ writeZeroes(len) {
+ this.write.call(this, new Uint8Array(Number(len)));
+ }
+ blockingWriteZeroes(len) {
+ this.blockingWrite.call(this, new Uint8Array(Number(len)));
+ }
+ blockingWriteZeroesAndFlush(len) {
+ this.blockingWriteAndFlush.call(this, new Uint8Array(Number(len)));
+ }
+ splice(src, len) {
+ const spliceLen = Math.min(len, this.checkWrite.call(this));
+ const bytes = src.read(spliceLen);
+ this.write.call(this, bytes);
+ return bytes.byteLength;
+ }
+ blockingSplice(_src, _len) {
+ console.log(`[streams] Blocking splice ${this.id}`);
+ }
+ forward(_src) {
+ console.log(`[streams] Forward ${this.id}`);
+ }
+ subscribe() {
+ console.log(`[streams] Subscribe to output stream ${this.id}`);
+ }
+ [symbolDispose]() {
+ }
+}
+
+export const error = { Error: IoError };
+
+export const streams = { InputStream, OutputStream };
+
+class Pollable {}
+
+function pollList (_list) {
+ // TODO
+}
+
+function pollOne (_poll) {
+ // TODO
+}
+
+export const poll = {
+ Pollable,
+ pollList,
+ pollOne
+};
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/random.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/random.js
new file mode 100644
index 0000000..0c47385
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/random.js
@@ -0,0 +1,56 @@
+const MAX_BYTES = 65536;
+
+let insecureRandomValue1, insecureRandomValue2;
+
+export const insecure = {
+ getInsecureRandomBytes (len) {
+ return random.getRandomBytes(len);
+ },
+ getInsecureRandomU64 () {
+ return random.getRandomU64();
+ }
+};
+
+let insecureSeedValue1, insecureSeedValue2;
+
+export const insecureSeed = {
+ insecureSeed () {
+ if (insecureSeedValue1 === undefined) {
+ insecureSeedValue1 = random.getRandomU64();
+ insecureSeedValue2 = random.getRandomU64();
+ }
+ return [insecureSeedValue1, insecureSeedValue2];
+ }
+};
+
+export const random = {
+ getRandomBytes(len) {
+ const bytes = new Uint8Array(Number(len));
+
+ if (len > MAX_BYTES) {
+ // this is the max bytes crypto.getRandomValues
+ // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues
+ for (var generated = 0; generated < len; generated += MAX_BYTES) {
+ // buffer.slice automatically checks if the end is past the end of
+ // the buffer so we don't have to here
+ crypto.getRandomValues(bytes.subarray(generated, generated + MAX_BYTES));
+ }
+ } else {
+ crypto.getRandomValues(bytes);
+ }
+
+ return bytes;
+ },
+
+ getRandomU64 () {
+ return crypto.getRandomValues(new BigUint64Array(1))[0];
+ },
+
+ insecureRandom () {
+ if (insecureRandomValue1 === undefined) {
+ insecureRandomValue1 = random.getRandomU64();
+ insecureRandomValue2 = random.getRandomU64();
+ }
+ return [insecureRandomValue1, insecureRandomValue2];
+ }
+};
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/sockets.js b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/sockets.js
new file mode 100644
index 0000000..6dda749
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/lib/browser/sockets.js
@@ -0,0 +1,186 @@
+export const instanceNetwork = {
+ instanceNetwork () {
+ console.log(`[sockets] instance network`);
+ }
+};
+
+export const ipNameLookup = {
+ dropResolveAddressStream () {
+
+ },
+ subscribe () {
+
+ },
+ resolveAddresses () {
+
+ },
+ resolveNextAddress () {
+
+ },
+ nonBlocking () {
+
+ },
+ setNonBlocking () {
+
+ },
+};
+
+export const network = {
+ dropNetwork () {
+
+ }
+};
+
+export const tcpCreateSocket = {
+ createTcpSocket () {
+
+ }
+};
+
+export const tcp = {
+ subscribe () {
+
+ },
+ dropTcpSocket() {
+
+ },
+ bind() {
+
+ },
+ connect() {
+
+ },
+ listen() {
+
+ },
+ accept() {
+
+ },
+ localAddress() {
+
+ },
+ remoteAddress() {
+
+ },
+ addressFamily() {
+
+ },
+ setListenBacklogSize() {
+
+ },
+ keepAlive() {
+
+ },
+ setKeepAlive() {
+
+ },
+ noDelay() {
+
+ },
+ setNoDelay() {
+
+ },
+ unicastHopLimit() {
+
+ },
+ setUnicastHopLimit() {
+
+ },
+ receiveBufferSize() {
+
+ },
+ setReceiveBufferSize() {
+
+ },
+ sendBufferSize() {
+
+ },
+ setSendBufferSize() {
+
+ },
+ nonBlocking() {
+
+ },
+ setNonBlocking() {
+
+ },
+ shutdown() {
+
+ }
+};
+
+export const udp = {
+ subscribe () {
+
+ },
+
+ dropUdpSocket () {
+
+ },
+
+ bind () {
+
+ },
+
+ connect () {
+
+ },
+
+ receive () {
+
+ },
+
+ send () {
+
+ },
+
+ localAddress () {
+
+ },
+
+ remoteAddress () {
+
+ },
+
+ addressFamily () {
+
+ },
+
+ unicastHopLimit () {
+
+ },
+
+ setUnicastHopLimit () {
+
+ },
+
+ receiveBufferSize () {
+
+ },
+
+ setReceiveBufferSize () {
+
+ },
+
+ sendBufferSize () {
+
+ },
+
+ setSendBufferSize () {
+
+ },
+
+ nonBlocking () {
+
+ },
+
+ setNonBlocking () {
+
+ }
+};
+
+export const udpCreateSocket = {
+ createUdpSocket () {
+
+ }
+};
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/package.json b/packages/web-host/overrides/@bytecodealliance/preview2-shim/package.json
new file mode 100644
index 0000000..ad0f53d
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "@bytecodealliance/preview2-shim",
+ "version": "0.17.2",
+ "description": "WASI Preview2 shim for JS environments",
+ "author": "Guy Bedford, Eduardo Rodrigues<16357187+eduardomourar@users.noreply.github.com>",
+ "type": "module",
+ "types": "./types/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./types/index.d.ts",
+ "node": "./lib/nodejs/index.js",
+ "default": "./lib/browser/index.js"
+ },
+ "./*": {
+ "types": "./types/*.d.ts",
+ "node": "./lib/nodejs/*.js",
+ "default": "./lib/browser/*.js"
+ }
+ },
+ "scripts": {
+ "test": "node --expose-gc ../../node_modules/mocha/bin/mocha.js -u tdd test/test.js --timeout 30000"
+ },
+ "files": [
+ "types",
+ "lib"
+ ],
+ "devDependencies": {
+ "mocha": "^10.2.0"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/bytecodealliance/jco.git"
+ },
+ "license": "(Apache-2.0 WITH LLVM-exception)",
+ "bugs": {
+ "url": "https://github.com/bytecodealliance/jco/issues"
+ },
+ "homepage": "https://github.com/bytecodealliance/jco#readme"
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/cli.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/cli.d.ts
new file mode 100644
index 0000000..25c66d7
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/cli.d.ts
@@ -0,0 +1,11 @@
+export type * as environment from './interfaces/wasi-cli-environment.d.ts';
+export type * as exit from './interfaces/wasi-cli-exit.d.ts';
+export type * as run from './interfaces/wasi-cli-run.d.ts';
+export type * as stderr from './interfaces/wasi-cli-stderr.d.ts';
+export type * as stdin from './interfaces/wasi-cli-stdin.d.ts';
+export type * as stdout from './interfaces/wasi-cli-stdout.d.ts';
+export type * as terminalInput from './interfaces/wasi-cli-terminal-input.d.ts';
+export type * as terminalOutput from './interfaces/wasi-cli-terminal-output.d.ts';
+export type * as terminalStderr from './interfaces/wasi-cli-terminal-stderr.d.ts';
+export type * as terminalStdin from './interfaces/wasi-cli-terminal-stdin.d.ts';
+export type * as terminalStdout from './interfaces/wasi-cli-terminal-stdout.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/clocks.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/clocks.d.ts
new file mode 100644
index 0000000..5924b07
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/clocks.d.ts
@@ -0,0 +1,2 @@
+export type * as monotonicClock from './interfaces/wasi-clocks-monotonic-clock.d.ts';
+export type * as wallClock from './interfaces/wasi-clocks-wall-clock.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/filesystem.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/filesystem.d.ts
new file mode 100644
index 0000000..0780816
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/filesystem.d.ts
@@ -0,0 +1,2 @@
+export type * as preopens from './interfaces/wasi-filesystem-preopens.d.ts';
+export type * as types from './interfaces/wasi-filesystem-types.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/http.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/http.d.ts
new file mode 100644
index 0000000..909d354
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/http.d.ts
@@ -0,0 +1,3 @@
+export type * as incomingHandler from './interfaces/wasi-http-incoming-handler.d.ts';
+export type * as outgoingHandler from './interfaces/wasi-http-outgoing-handler.d.ts';
+export type * as types from './interfaces/wasi-http-types.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/index.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/index.d.ts
new file mode 100644
index 0000000..aeed2d5
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/index.d.ts
@@ -0,0 +1,7 @@
+export type * as cli from "./cli.d.ts";
+export type * as clocks from './clocks.d.ts';
+export type * as filesystem from './filesystem.d.ts';
+export type * as http from "./http.d.ts";
+export type * as io from "./io.d.ts";
+export type * as random from "./random.d.ts";
+export type * as sockets from "./sockets.d.ts";
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-environment.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-environment.d.ts
new file mode 100644
index 0000000..45f685c
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-environment.d.ts
@@ -0,0 +1,21 @@
+/** @module Interface wasi:cli/environment@0.2.3 **/
+/**
+ * Get the POSIX-style environment variables.
+ *
+ * Each environment variable is provided as a pair of string variable names
+ * and string value.
+ *
+ * Morally, these are a value import, but until value imports are available
+ * in the component model, this import function should return the same
+ * values each time it is called.
+ */
+export function getEnvironment(): Array<[string, string]>;
+/**
+ * Get the POSIX-style arguments to the program.
+ */
+export function getArguments(): Array;
+/**
+ * Return a path that programs should use as their initial current working
+ * directory, interpreting `.` as shorthand for this.
+ */
+export function initialCwd(): string | undefined;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-exit.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-exit.d.ts
new file mode 100644
index 0000000..5ae4465
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-exit.d.ts
@@ -0,0 +1,6 @@
+/** @module Interface wasi:cli/exit@0.2.3 **/
+/**
+ * Exit the current instance and any linked instances.
+ */
+export function exit(status: Result): void;
+export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E };
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-run.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-run.d.ts
new file mode 100644
index 0000000..5f604f5
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-run.d.ts
@@ -0,0 +1,5 @@
+/** @module Interface wasi:cli/run@0.2.3 **/
+/**
+ * Run the program.
+ */
+export function run(): void;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stderr.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stderr.d.ts
new file mode 100644
index 0000000..4439d41
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stderr.d.ts
@@ -0,0 +1,3 @@
+/** @module Interface wasi:cli/stderr@0.2.3 **/
+export function getStderr(): OutputStream;
+export type OutputStream = import('./wasi-io-streams.js').OutputStream;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stdin.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stdin.d.ts
new file mode 100644
index 0000000..96b7108
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stdin.d.ts
@@ -0,0 +1,3 @@
+/** @module Interface wasi:cli/stdin@0.2.3 **/
+export function getStdin(): InputStream;
+export type InputStream = import('./wasi-io-streams.js').InputStream;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stdout.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stdout.d.ts
new file mode 100644
index 0000000..f4ba8ce
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-stdout.d.ts
@@ -0,0 +1,3 @@
+/** @module Interface wasi:cli/stdout@0.2.3 **/
+export function getStdout(): OutputStream;
+export type OutputStream = import('./wasi-io-streams.js').OutputStream;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-input.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-input.d.ts
new file mode 100644
index 0000000..12f18cc
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-input.d.ts
@@ -0,0 +1,8 @@
+/** @module Interface wasi:cli/terminal-input@0.2.3 **/
+
+export class TerminalInput {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-output.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-output.d.ts
new file mode 100644
index 0000000..43f9b0f
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-output.d.ts
@@ -0,0 +1,8 @@
+/** @module Interface wasi:cli/terminal-output@0.2.3 **/
+
+export class TerminalOutput {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stderr.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stderr.d.ts
new file mode 100644
index 0000000..0b01576
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stderr.d.ts
@@ -0,0 +1,7 @@
+/** @module Interface wasi:cli/terminal-stderr@0.2.3 **/
+/**
+ * If stderr is connected to a terminal, return a `terminal-output` handle
+ * allowing further interaction with it.
+ */
+export function getTerminalStderr(): TerminalOutput | undefined;
+export type TerminalOutput = import('./wasi-cli-terminal-output.js').TerminalOutput;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stdin.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stdin.d.ts
new file mode 100644
index 0000000..c01afc5
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stdin.d.ts
@@ -0,0 +1,7 @@
+/** @module Interface wasi:cli/terminal-stdin@0.2.3 **/
+/**
+ * If stdin is connected to a terminal, return a `terminal-input` handle
+ * allowing further interaction with it.
+ */
+export function getTerminalStdin(): TerminalInput | undefined;
+export type TerminalInput = import('./wasi-cli-terminal-input.js').TerminalInput;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stdout.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stdout.d.ts
new file mode 100644
index 0000000..6dc6e92
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-cli-terminal-stdout.d.ts
@@ -0,0 +1,7 @@
+/** @module Interface wasi:cli/terminal-stdout@0.2.3 **/
+/**
+ * If stdout is connected to a terminal, return a `terminal-output` handle
+ * allowing further interaction with it.
+ */
+export function getTerminalStdout(): TerminalOutput | undefined;
+export type TerminalOutput = import('./wasi-cli-terminal-output.js').TerminalOutput;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-clocks-monotonic-clock.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-clocks-monotonic-clock.d.ts
new file mode 100644
index 0000000..5a6ec46
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-clocks-monotonic-clock.d.ts
@@ -0,0 +1,34 @@
+/** @module Interface wasi:clocks/monotonic-clock@0.2.3 **/
+/**
+ * Read the current value of the clock.
+ *
+ * The clock is monotonic, therefore calling this function repeatedly will
+ * produce a sequence of non-decreasing values.
+ */
+export function now(): Instant;
+/**
+ * Query the resolution of the clock. Returns the duration of time
+ * corresponding to a clock tick.
+ */
+export function resolution(): Duration;
+/**
+ * Create a `pollable` which will resolve once the specified instant
+ * has occurred.
+ */
+export function subscribeInstant(when: Instant): Pollable;
+/**
+ * Create a `pollable` that will resolve after the specified duration has
+ * elapsed from the time this function is invoked.
+ */
+export function subscribeDuration(when: Duration): Pollable;
+export type Pollable = import('./wasi-io-poll.js').Pollable;
+/**
+ * An instant in time, in nanoseconds. An instant is relative to an
+ * unspecified initial value, and can only be compared to instances from
+ * the same monotonic-clock.
+ */
+export type Instant = bigint;
+/**
+ * A duration of time, in nanoseconds.
+ */
+export type Duration = bigint;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-clocks-wall-clock.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-clocks-wall-clock.d.ts
new file mode 100644
index 0000000..9310603
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-clocks-wall-clock.d.ts
@@ -0,0 +1,30 @@
+/** @module Interface wasi:clocks/wall-clock@0.2.3 **/
+/**
+ * Read the current value of the clock.
+ *
+ * This clock is not monotonic, therefore calling this function repeatedly
+ * will not necessarily produce a sequence of non-decreasing values.
+ *
+ * The returned timestamps represent the number of seconds since
+ * 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch],
+ * also known as [Unix Time].
+ *
+ * The nanoseconds field of the output is always less than 1000000000.
+ *
+ * [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16
+ * [Unix Time]: https://en.wikipedia.org/wiki/Unix_time
+ */
+export function now(): Datetime;
+/**
+ * Query the resolution of the clock.
+ *
+ * The nanoseconds field of the output is always less than 1000000000.
+ */
+export function resolution(): Datetime;
+/**
+ * A time and date in seconds plus nanoseconds.
+ */
+export interface Datetime {
+ seconds: bigint,
+ nanoseconds: number,
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-filesystem-preopens.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-filesystem-preopens.d.ts
new file mode 100644
index 0000000..cca6e6b
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-filesystem-preopens.d.ts
@@ -0,0 +1,6 @@
+/** @module Interface wasi:filesystem/preopens@0.2.3 **/
+/**
+ * Return the set of preopened directories, and their paths.
+ */
+export function getDirectories(): Array<[Descriptor, string]>;
+export type Descriptor = import('./wasi-filesystem-types.js').Descriptor;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-filesystem-types.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-filesystem-types.d.ts
new file mode 100644
index 0000000..cf1f580
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-filesystem-types.d.ts
@@ -0,0 +1,669 @@
+/** @module Interface wasi:filesystem/types@0.2.3 **/
+/**
+ * Attempts to extract a filesystem-related `error-code` from the stream
+ * `error` provided.
+ *
+ * Stream operations which return `stream-error::last-operation-failed`
+ * have a payload with more information about the operation that failed.
+ * This payload can be passed through to this function to see if there's
+ * filesystem-related information about the error to return.
+ *
+ * Note that this function is fallible because not all stream-related
+ * errors are filesystem-related errors.
+ */
+export function filesystemErrorCode(err: Error): ErrorCode | undefined;
+export type InputStream = import('./wasi-io-streams.js').InputStream;
+export type OutputStream = import('./wasi-io-streams.js').OutputStream;
+export type Error = import('./wasi-io-streams.js').Error;
+export type Datetime = import('./wasi-clocks-wall-clock.js').Datetime;
+/**
+ * File size or length of a region within a file.
+ */
+export type Filesize = bigint;
+/**
+ * The type of a filesystem object referenced by a descriptor.
+ *
+ * Note: This was called `filetype` in earlier versions of WASI.
+ * # Variants
+ *
+ * ## `"unknown"`
+ *
+ * The type of the descriptor or file is unknown or is different from
+ * any of the other types specified.
+ * ## `"block-device"`
+ *
+ * The descriptor refers to a block device inode.
+ * ## `"character-device"`
+ *
+ * The descriptor refers to a character device inode.
+ * ## `"directory"`
+ *
+ * The descriptor refers to a directory inode.
+ * ## `"fifo"`
+ *
+ * The descriptor refers to a named pipe.
+ * ## `"symbolic-link"`
+ *
+ * The file refers to a symbolic link inode.
+ * ## `"regular-file"`
+ *
+ * The descriptor refers to a regular file inode.
+ * ## `"socket"`
+ *
+ * The descriptor refers to a socket.
+ */
+export type DescriptorType = 'unknown' | 'block-device' | 'character-device' | 'directory' | 'fifo' | 'symbolic-link' | 'regular-file' | 'socket';
+/**
+ * Descriptor flags.
+ *
+ * Note: This was called `fdflags` in earlier versions of WASI.
+ */
+export interface DescriptorFlags {
+ /**
+ * Read mode: Data can be read.
+ */
+ read?: boolean,
+ /**
+ * Write mode: Data can be written to.
+ */
+ write?: boolean,
+ /**
+ * Request that writes be performed according to synchronized I/O file
+ * integrity completion. The data stored in the file and the file's
+ * metadata are synchronized. This is similar to `O_SYNC` in POSIX.
+ *
+ * The precise semantics of this operation have not yet been defined for
+ * WASI. At this time, it should be interpreted as a request, and not a
+ * requirement.
+ */
+ fileIntegritySync?: boolean,
+ /**
+ * Request that writes be performed according to synchronized I/O data
+ * integrity completion. Only the data stored in the file is
+ * synchronized. This is similar to `O_DSYNC` in POSIX.
+ *
+ * The precise semantics of this operation have not yet been defined for
+ * WASI. At this time, it should be interpreted as a request, and not a
+ * requirement.
+ */
+ dataIntegritySync?: boolean,
+ /**
+ * Requests that reads be performed at the same level of integrity
+ * requested for writes. This is similar to `O_RSYNC` in POSIX.
+ *
+ * The precise semantics of this operation have not yet been defined for
+ * WASI. At this time, it should be interpreted as a request, and not a
+ * requirement.
+ */
+ requestedWriteSync?: boolean,
+ /**
+ * Mutating directories mode: Directory contents may be mutated.
+ *
+ * When this flag is unset on a descriptor, operations using the
+ * descriptor which would create, rename, delete, modify the data or
+ * metadata of filesystem objects, or obtain another handle which
+ * would permit any of those, shall fail with `error-code::read-only` if
+ * they would otherwise succeed.
+ *
+ * This may only be set on directories.
+ */
+ mutateDirectory?: boolean,
+}
+/**
+ * Flags determining the method of how paths are resolved.
+ */
+export interface PathFlags {
+ /**
+ * As long as the resolved path corresponds to a symbolic link, it is
+ * expanded.
+ */
+ symlinkFollow?: boolean,
+}
+/**
+ * Open flags used by `open-at`.
+ */
+export interface OpenFlags {
+ /**
+ * Create file if it does not exist, similar to `O_CREAT` in POSIX.
+ */
+ create?: boolean,
+ /**
+ * Fail if not a directory, similar to `O_DIRECTORY` in POSIX.
+ */
+ directory?: boolean,
+ /**
+ * Fail if file already exists, similar to `O_EXCL` in POSIX.
+ */
+ exclusive?: boolean,
+ /**
+ * Truncate file to size 0, similar to `O_TRUNC` in POSIX.
+ */
+ truncate?: boolean,
+}
+/**
+ * Number of hard links to an inode.
+ */
+export type LinkCount = bigint;
+/**
+ * File attributes.
+ *
+ * Note: This was called `filestat` in earlier versions of WASI.
+ */
+export interface DescriptorStat {
+ /**
+ * File type.
+ */
+ type: DescriptorType,
+ /**
+ * Number of hard links to the file.
+ */
+ linkCount: LinkCount,
+ /**
+ * For regular files, the file size in bytes. For symbolic links, the
+ * length in bytes of the pathname contained in the symbolic link.
+ */
+ size: Filesize,
+ /**
+ * Last data access timestamp.
+ *
+ * If the `option` is none, the platform doesn't maintain an access
+ * timestamp for this file.
+ */
+ dataAccessTimestamp?: Datetime,
+ /**
+ * Last data modification timestamp.
+ *
+ * If the `option` is none, the platform doesn't maintain a
+ * modification timestamp for this file.
+ */
+ dataModificationTimestamp?: Datetime,
+ /**
+ * Last file status-change timestamp.
+ *
+ * If the `option` is none, the platform doesn't maintain a
+ * status-change timestamp for this file.
+ */
+ statusChangeTimestamp?: Datetime,
+}
+/**
+ * When setting a timestamp, this gives the value to set it to.
+ */
+export type NewTimestamp = NewTimestampNoChange | NewTimestampNow | NewTimestampTimestamp;
+/**
+ * Leave the timestamp set to its previous value.
+ */
+export interface NewTimestampNoChange {
+ tag: 'no-change',
+}
+/**
+ * Set the timestamp to the current time of the system clock associated
+ * with the filesystem.
+ */
+export interface NewTimestampNow {
+ tag: 'now',
+}
+/**
+ * Set the timestamp to the given value.
+ */
+export interface NewTimestampTimestamp {
+ tag: 'timestamp',
+ val: Datetime,
+}
+/**
+ * A directory entry.
+ */
+export interface DirectoryEntry {
+ /**
+ * The type of the file referred to by this directory entry.
+ */
+ type: DescriptorType,
+ /**
+ * The name of the object.
+ */
+ name: string,
+}
+/**
+ * Error codes returned by functions, similar to `errno` in POSIX.
+ * Not all of these error codes are returned by the functions provided by this
+ * API; some are used in higher-level library layers, and others are provided
+ * merely for alignment with POSIX.
+ * # Variants
+ *
+ * ## `"access"`
+ *
+ * Permission denied, similar to `EACCES` in POSIX.
+ * ## `"would-block"`
+ *
+ * Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX.
+ * ## `"already"`
+ *
+ * Connection already in progress, similar to `EALREADY` in POSIX.
+ * ## `"bad-descriptor"`
+ *
+ * Bad descriptor, similar to `EBADF` in POSIX.
+ * ## `"busy"`
+ *
+ * Device or resource busy, similar to `EBUSY` in POSIX.
+ * ## `"deadlock"`
+ *
+ * Resource deadlock would occur, similar to `EDEADLK` in POSIX.
+ * ## `"quota"`
+ *
+ * Storage quota exceeded, similar to `EDQUOT` in POSIX.
+ * ## `"exist"`
+ *
+ * File exists, similar to `EEXIST` in POSIX.
+ * ## `"file-too-large"`
+ *
+ * File too large, similar to `EFBIG` in POSIX.
+ * ## `"illegal-byte-sequence"`
+ *
+ * Illegal byte sequence, similar to `EILSEQ` in POSIX.
+ * ## `"in-progress"`
+ *
+ * Operation in progress, similar to `EINPROGRESS` in POSIX.
+ * ## `"interrupted"`
+ *
+ * Interrupted function, similar to `EINTR` in POSIX.
+ * ## `"invalid"`
+ *
+ * Invalid argument, similar to `EINVAL` in POSIX.
+ * ## `"io"`
+ *
+ * I/O error, similar to `EIO` in POSIX.
+ * ## `"is-directory"`
+ *
+ * Is a directory, similar to `EISDIR` in POSIX.
+ * ## `"loop"`
+ *
+ * Too many levels of symbolic links, similar to `ELOOP` in POSIX.
+ * ## `"too-many-links"`
+ *
+ * Too many links, similar to `EMLINK` in POSIX.
+ * ## `"message-size"`
+ *
+ * Message too large, similar to `EMSGSIZE` in POSIX.
+ * ## `"name-too-long"`
+ *
+ * Filename too long, similar to `ENAMETOOLONG` in POSIX.
+ * ## `"no-device"`
+ *
+ * No such device, similar to `ENODEV` in POSIX.
+ * ## `"no-entry"`
+ *
+ * No such file or directory, similar to `ENOENT` in POSIX.
+ * ## `"no-lock"`
+ *
+ * No locks available, similar to `ENOLCK` in POSIX.
+ * ## `"insufficient-memory"`
+ *
+ * Not enough space, similar to `ENOMEM` in POSIX.
+ * ## `"insufficient-space"`
+ *
+ * No space left on device, similar to `ENOSPC` in POSIX.
+ * ## `"not-directory"`
+ *
+ * Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX.
+ * ## `"not-empty"`
+ *
+ * Directory not empty, similar to `ENOTEMPTY` in POSIX.
+ * ## `"not-recoverable"`
+ *
+ * State not recoverable, similar to `ENOTRECOVERABLE` in POSIX.
+ * ## `"unsupported"`
+ *
+ * Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX.
+ * ## `"no-tty"`
+ *
+ * Inappropriate I/O control operation, similar to `ENOTTY` in POSIX.
+ * ## `"no-such-device"`
+ *
+ * No such device or address, similar to `ENXIO` in POSIX.
+ * ## `"overflow"`
+ *
+ * Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX.
+ * ## `"not-permitted"`
+ *
+ * Operation not permitted, similar to `EPERM` in POSIX.
+ * ## `"pipe"`
+ *
+ * Broken pipe, similar to `EPIPE` in POSIX.
+ * ## `"read-only"`
+ *
+ * Read-only file system, similar to `EROFS` in POSIX.
+ * ## `"invalid-seek"`
+ *
+ * Invalid seek, similar to `ESPIPE` in POSIX.
+ * ## `"text-file-busy"`
+ *
+ * Text file busy, similar to `ETXTBSY` in POSIX.
+ * ## `"cross-device"`
+ *
+ * Cross-device link, similar to `EXDEV` in POSIX.
+ */
+export type ErrorCode = 'access' | 'would-block' | 'already' | 'bad-descriptor' | 'busy' | 'deadlock' | 'quota' | 'exist' | 'file-too-large' | 'illegal-byte-sequence' | 'in-progress' | 'interrupted' | 'invalid' | 'io' | 'is-directory' | 'loop' | 'too-many-links' | 'message-size' | 'name-too-long' | 'no-device' | 'no-entry' | 'no-lock' | 'insufficient-memory' | 'insufficient-space' | 'not-directory' | 'not-empty' | 'not-recoverable' | 'unsupported' | 'no-tty' | 'no-such-device' | 'overflow' | 'not-permitted' | 'pipe' | 'read-only' | 'invalid-seek' | 'text-file-busy' | 'cross-device';
+/**
+ * File or memory access pattern advisory information.
+ * # Variants
+ *
+ * ## `"normal"`
+ *
+ * The application has no advice to give on its behavior with respect
+ * to the specified data.
+ * ## `"sequential"`
+ *
+ * The application expects to access the specified data sequentially
+ * from lower offsets to higher offsets.
+ * ## `"random"`
+ *
+ * The application expects to access the specified data in a random
+ * order.
+ * ## `"will-need"`
+ *
+ * The application expects to access the specified data in the near
+ * future.
+ * ## `"dont-need"`
+ *
+ * The application expects that it will not access the specified data
+ * in the near future.
+ * ## `"no-reuse"`
+ *
+ * The application expects to access the specified data once and then
+ * not reuse it thereafter.
+ */
+export type Advice = 'normal' | 'sequential' | 'random' | 'will-need' | 'dont-need' | 'no-reuse';
+/**
+ * A 128-bit hash value, split into parts because wasm doesn't have a
+ * 128-bit integer type.
+ */
+export interface MetadataHashValue {
+ /**
+ * 64 bits of a 128-bit hash value.
+ */
+ lower: bigint,
+ /**
+ * Another 64 bits of a 128-bit hash value.
+ */
+ upper: bigint,
+}
+
+export class Descriptor {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Return a stream for reading from a file, if available.
+ *
+ * May fail with an error-code describing why the file cannot be read.
+ *
+ * Multiple read, write, and append streams may be active on the same open
+ * file and they do not interfere with each other.
+ *
+ * Note: This allows using `read-stream`, which is similar to `read` in POSIX.
+ */
+ readViaStream(offset: Filesize): InputStream;
+ /**
+ * Return a stream for writing to a file, if available.
+ *
+ * May fail with an error-code describing why the file cannot be written.
+ *
+ * Note: This allows using `write-stream`, which is similar to `write` in
+ * POSIX.
+ */
+ writeViaStream(offset: Filesize): OutputStream;
+ /**
+ * Return a stream for appending to a file, if available.
+ *
+ * May fail with an error-code describing why the file cannot be appended.
+ *
+ * Note: This allows using `write-stream`, which is similar to `write` with
+ * `O_APPEND` in POSIX.
+ */
+ appendViaStream(): OutputStream;
+ /**
+ * Provide file advisory information on a descriptor.
+ *
+ * This is similar to `posix_fadvise` in POSIX.
+ */
+ advise(offset: Filesize, length: Filesize, advice: Advice): void;
+ /**
+ * Synchronize the data of a file to disk.
+ *
+ * This function succeeds with no effect if the file descriptor is not
+ * opened for writing.
+ *
+ * Note: This is similar to `fdatasync` in POSIX.
+ */
+ syncData(): void;
+ /**
+ * Get flags associated with a descriptor.
+ *
+ * Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX.
+ *
+ * Note: This returns the value that was the `fs_flags` value returned
+ * from `fdstat_get` in earlier versions of WASI.
+ */
+ getFlags(): DescriptorFlags;
+ /**
+ * Get the dynamic type of a descriptor.
+ *
+ * Note: This returns the same value as the `type` field of the `fd-stat`
+ * returned by `stat`, `stat-at` and similar.
+ *
+ * Note: This returns similar flags to the `st_mode & S_IFMT` value provided
+ * by `fstat` in POSIX.
+ *
+ * Note: This returns the value that was the `fs_filetype` value returned
+ * from `fdstat_get` in earlier versions of WASI.
+ */
+ getType(): DescriptorType;
+ /**
+ * Adjust the size of an open file. If this increases the file's size, the
+ * extra bytes are filled with zeros.
+ *
+ * Note: This was called `fd_filestat_set_size` in earlier versions of WASI.
+ */
+ setSize(size: Filesize): void;
+ /**
+ * Adjust the timestamps of an open file or directory.
+ *
+ * Note: This is similar to `futimens` in POSIX.
+ *
+ * Note: This was called `fd_filestat_set_times` in earlier versions of WASI.
+ */
+ setTimes(dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void;
+ /**
+ * Read from a descriptor, without using and updating the descriptor's offset.
+ *
+ * This function returns a list of bytes containing the data that was
+ * read, along with a bool which, when true, indicates that the end of the
+ * file was reached. The returned list will contain up to `length` bytes; it
+ * may return fewer than requested, if the end of the file is reached or
+ * if the I/O operation is interrupted.
+ *
+ * In the future, this may change to return a `stream`.
+ *
+ * Note: This is similar to `pread` in POSIX.
+ */
+ read(length: Filesize, offset: Filesize): [Uint8Array, boolean];
+ /**
+ * Write to a descriptor, without using and updating the descriptor's offset.
+ *
+ * It is valid to write past the end of a file; the file is extended to the
+ * extent of the write, with bytes between the previous end and the start of
+ * the write set to zero.
+ *
+ * In the future, this may change to take a `stream`.
+ *
+ * Note: This is similar to `pwrite` in POSIX.
+ */
+ write(buffer: Uint8Array, offset: Filesize): Filesize;
+ /**
+ * Read directory entries from a directory.
+ *
+ * On filesystems where directories contain entries referring to themselves
+ * and their parents, often named `.` and `..` respectively, these entries
+ * are omitted.
+ *
+ * This always returns a new stream which starts at the beginning of the
+ * directory. Multiple streams may be active on the same directory, and they
+ * do not interfere with each other.
+ */
+ readDirectory(): DirectoryEntryStream;
+ /**
+ * Synchronize the data and metadata of a file to disk.
+ *
+ * This function succeeds with no effect if the file descriptor is not
+ * opened for writing.
+ *
+ * Note: This is similar to `fsync` in POSIX.
+ */
+ sync(): void;
+ /**
+ * Create a directory.
+ *
+ * Note: This is similar to `mkdirat` in POSIX.
+ */
+ createDirectoryAt(path: string): void;
+ /**
+ * Return the attributes of an open file or directory.
+ *
+ * Note: This is similar to `fstat` in POSIX, except that it does not return
+ * device and inode information. For testing whether two descriptors refer to
+ * the same underlying filesystem object, use `is-same-object`. To obtain
+ * additional data that can be used do determine whether a file has been
+ * modified, use `metadata-hash`.
+ *
+ * Note: This was called `fd_filestat_get` in earlier versions of WASI.
+ */
+ stat(): DescriptorStat;
+ /**
+ * Return the attributes of a file or directory.
+ *
+ * Note: This is similar to `fstatat` in POSIX, except that it does not
+ * return device and inode information. See the `stat` description for a
+ * discussion of alternatives.
+ *
+ * Note: This was called `path_filestat_get` in earlier versions of WASI.
+ */
+ statAt(pathFlags: PathFlags, path: string): DescriptorStat;
+ /**
+ * Adjust the timestamps of a file or directory.
+ *
+ * Note: This is similar to `utimensat` in POSIX.
+ *
+ * Note: This was called `path_filestat_set_times` in earlier versions of
+ * WASI.
+ */
+ setTimesAt(pathFlags: PathFlags, path: string, dataAccessTimestamp: NewTimestamp, dataModificationTimestamp: NewTimestamp): void;
+ /**
+ * Create a hard link.
+ *
+ * Note: This is similar to `linkat` in POSIX.
+ */
+ linkAt(oldPathFlags: PathFlags, oldPath: string, newDescriptor: Descriptor, newPath: string): void;
+ /**
+ * Open a file or directory.
+ *
+ * If `flags` contains `descriptor-flags::mutate-directory`, and the base
+ * descriptor doesn't have `descriptor-flags::mutate-directory` set,
+ * `open-at` fails with `error-code::read-only`.
+ *
+ * If `flags` contains `write` or `mutate-directory`, or `open-flags`
+ * contains `truncate` or `create`, and the base descriptor doesn't have
+ * `descriptor-flags::mutate-directory` set, `open-at` fails with
+ * `error-code::read-only`.
+ *
+ * Note: This is similar to `openat` in POSIX.
+ */
+ openAt(pathFlags: PathFlags, path: string, openFlags: OpenFlags, flags: DescriptorFlags): Descriptor;
+ /**
+ * Read the contents of a symbolic link.
+ *
+ * If the contents contain an absolute or rooted path in the underlying
+ * filesystem, this function fails with `error-code::not-permitted`.
+ *
+ * Note: This is similar to `readlinkat` in POSIX.
+ */
+ readlinkAt(path: string): string;
+ /**
+ * Remove a directory.
+ *
+ * Return `error-code::not-empty` if the directory is not empty.
+ *
+ * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+ */
+ removeDirectoryAt(path: string): void;
+ /**
+ * Rename a filesystem object.
+ *
+ * Note: This is similar to `renameat` in POSIX.
+ */
+ renameAt(oldPath: string, newDescriptor: Descriptor, newPath: string): void;
+ /**
+ * Create a symbolic link (also known as a "symlink").
+ *
+ * If `old-path` starts with `/`, the function fails with
+ * `error-code::not-permitted`.
+ *
+ * Note: This is similar to `symlinkat` in POSIX.
+ */
+ symlinkAt(oldPath: string, newPath: string): void;
+ /**
+ * Unlink a filesystem object that is not a directory.
+ *
+ * Return `error-code::is-directory` if the path refers to a directory.
+ * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+ */
+ unlinkFileAt(path: string): void;
+ /**
+ * Test whether two descriptors refer to the same filesystem object.
+ *
+ * In POSIX, this corresponds to testing whether the two descriptors have the
+ * same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers.
+ * wasi-filesystem does not expose device and inode numbers, so this function
+ * may be used instead.
+ */
+ isSameObject(other: Descriptor): boolean;
+ /**
+ * Return a hash of the metadata associated with a filesystem object referred
+ * to by a descriptor.
+ *
+ * This returns a hash of the last-modification timestamp and file size, and
+ * may also include the inode number, device number, birth timestamp, and
+ * other metadata fields that may change when the file is modified or
+ * replaced. It may also include a secret value chosen by the
+ * implementation and not otherwise exposed.
+ *
+ * Implementations are encouraged to provide the following properties:
+ *
+ * - If the file is not modified or replaced, the computed hash value should
+ * usually not change.
+ * - If the object is modified or replaced, the computed hash value should
+ * usually change.
+ * - The inputs to the hash should not be easily computable from the
+ * computed hash.
+ *
+ * However, none of these is required.
+ */
+ metadataHash(): MetadataHashValue;
+ /**
+ * Return a hash of the metadata associated with a filesystem object referred
+ * to by a directory descriptor and a relative path.
+ *
+ * This performs the same hash computation as `metadata-hash`.
+ */
+ metadataHashAt(pathFlags: PathFlags, path: string): MetadataHashValue;
+}
+
+export class DirectoryEntryStream {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Read a single directory entry from a `directory-entry-stream`.
+ */
+ readDirectoryEntry(): DirectoryEntry | undefined;
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-incoming-handler.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-incoming-handler.d.ts
new file mode 100644
index 0000000..3a1ffb1
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-incoming-handler.d.ts
@@ -0,0 +1,16 @@
+/** @module Interface wasi:http/incoming-handler@0.2.3 **/
+/**
+ * This function is invoked with an incoming HTTP Request, and a resource
+ * `response-outparam` which provides the capability to reply with an HTTP
+ * Response. The response is sent by calling the `response-outparam.set`
+ * method, which allows execution to continue after the response has been
+ * sent. This enables both streaming to the response body, and performing other
+ * work.
+ *
+ * The implementor of this function must write a response to the
+ * `response-outparam` before returning, or else the caller will respond
+ * with an error on its behalf.
+ */
+export function handle(request: IncomingRequest, responseOut: ResponseOutparam): void;
+export type IncomingRequest = import('./wasi-http-types.js').IncomingRequest;
+export type ResponseOutparam = import('./wasi-http-types.js').ResponseOutparam;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-outgoing-handler.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-outgoing-handler.d.ts
new file mode 100644
index 0000000..19566e9
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-outgoing-handler.d.ts
@@ -0,0 +1,18 @@
+/** @module Interface wasi:http/outgoing-handler@0.2.3 **/
+/**
+ * This function is invoked with an outgoing HTTP Request, and it returns
+ * a resource `future-incoming-response` which represents an HTTP Response
+ * which may arrive in the future.
+ *
+ * The `options` argument accepts optional parameters for the HTTP
+ * protocol's transport layer.
+ *
+ * This function may return an error if the `outgoing-request` is invalid
+ * or not allowed to be made. Otherwise, protocol errors are reported
+ * through the `future-incoming-response`.
+ */
+export function handle(request: OutgoingRequest, options: RequestOptions | undefined): FutureIncomingResponse;
+export type OutgoingRequest = import('./wasi-http-types.js').OutgoingRequest;
+export type RequestOptions = import('./wasi-http-types.js').RequestOptions;
+export type FutureIncomingResponse = import('./wasi-http-types.js').FutureIncomingResponse;
+export type ErrorCode = import('./wasi-http-types.js').ErrorCode;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-types.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-types.d.ts
new file mode 100644
index 0000000..6116667
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-http-types.d.ts
@@ -0,0 +1,748 @@
+/** @module Interface wasi:http/types@0.2.3 **/
+/**
+ * Attempts to extract a http-related `error` from the wasi:io `error`
+ * provided.
+ *
+ * Stream operations which return
+ * `wasi:io/stream/stream-error::last-operation-failed` have a payload of
+ * type `wasi:io/error/error` with more information about the operation
+ * that failed. This payload can be passed through to this function to see
+ * if there's http-related information about the error to return.
+ *
+ * Note that this function is fallible because not all io-errors are
+ * http-related errors.
+ */
+export function httpErrorCode(err: IoError): ErrorCode | undefined;
+export type Duration = import('./wasi-clocks-monotonic-clock.js').Duration;
+export type InputStream = import('./wasi-io-streams.js').InputStream;
+export type OutputStream = import('./wasi-io-streams.js').OutputStream;
+export type IoError = import('./wasi-io-error.js').Error;
+export type Pollable = import('./wasi-io-poll.js').Pollable;
+/**
+ * This type corresponds to HTTP standard Methods.
+ */
+export type Method = MethodGet | MethodHead | MethodPost | MethodPut | MethodDelete | MethodConnect | MethodOptions | MethodTrace | MethodPatch | MethodOther;
+export interface MethodGet {
+ tag: 'get',
+}
+export interface MethodHead {
+ tag: 'head',
+}
+export interface MethodPost {
+ tag: 'post',
+}
+export interface MethodPut {
+ tag: 'put',
+}
+export interface MethodDelete {
+ tag: 'delete',
+}
+export interface MethodConnect {
+ tag: 'connect',
+}
+export interface MethodOptions {
+ tag: 'options',
+}
+export interface MethodTrace {
+ tag: 'trace',
+}
+export interface MethodPatch {
+ tag: 'patch',
+}
+export interface MethodOther {
+ tag: 'other',
+ val: string,
+}
+/**
+ * This type corresponds to HTTP standard Related Schemes.
+ */
+export type Scheme = SchemeHttp | SchemeHttps | SchemeOther;
+export interface SchemeHttp {
+ tag: 'HTTP',
+}
+export interface SchemeHttps {
+ tag: 'HTTPS',
+}
+export interface SchemeOther {
+ tag: 'other',
+ val: string,
+}
+/**
+ * Defines the case payload type for `DNS-error` above:
+ */
+export interface DnsErrorPayload {
+ rcode?: string,
+ infoCode?: number,
+}
+/**
+ * Defines the case payload type for `TLS-alert-received` above:
+ */
+export interface TlsAlertReceivedPayload {
+ alertId?: number,
+ alertMessage?: string,
+}
+/**
+ * Defines the case payload type for `HTTP-response-{header,trailer}-size` above:
+ */
+export interface FieldSizePayload {
+ fieldName?: string,
+ fieldSize?: number,
+}
+/**
+ * These cases are inspired by the IANA HTTP Proxy Error Types:
+ *
+ */
+export type ErrorCode = ErrorCodeDnsTimeout | ErrorCodeDnsError | ErrorCodeDestinationNotFound | ErrorCodeDestinationUnavailable | ErrorCodeDestinationIpProhibited | ErrorCodeDestinationIpUnroutable | ErrorCodeConnectionRefused | ErrorCodeConnectionTerminated | ErrorCodeConnectionTimeout | ErrorCodeConnectionReadTimeout | ErrorCodeConnectionWriteTimeout | ErrorCodeConnectionLimitReached | ErrorCodeTlsProtocolError | ErrorCodeTlsCertificateError | ErrorCodeTlsAlertReceived | ErrorCodeHttpRequestDenied | ErrorCodeHttpRequestLengthRequired | ErrorCodeHttpRequestBodySize | ErrorCodeHttpRequestMethodInvalid | ErrorCodeHttpRequestUriInvalid | ErrorCodeHttpRequestUriTooLong | ErrorCodeHttpRequestHeaderSectionSize | ErrorCodeHttpRequestHeaderSize | ErrorCodeHttpRequestTrailerSectionSize | ErrorCodeHttpRequestTrailerSize | ErrorCodeHttpResponseIncomplete | ErrorCodeHttpResponseHeaderSectionSize | ErrorCodeHttpResponseHeaderSize | ErrorCodeHttpResponseBodySize | ErrorCodeHttpResponseTrailerSectionSize | ErrorCodeHttpResponseTrailerSize | ErrorCodeHttpResponseTransferCoding | ErrorCodeHttpResponseContentCoding | ErrorCodeHttpResponseTimeout | ErrorCodeHttpUpgradeFailed | ErrorCodeHttpProtocolError | ErrorCodeLoopDetected | ErrorCodeConfigurationError | ErrorCodeInternalError;
+export interface ErrorCodeDnsTimeout {
+ tag: 'DNS-timeout',
+}
+export interface ErrorCodeDnsError {
+ tag: 'DNS-error',
+ val: DnsErrorPayload,
+}
+export interface ErrorCodeDestinationNotFound {
+ tag: 'destination-not-found',
+}
+export interface ErrorCodeDestinationUnavailable {
+ tag: 'destination-unavailable',
+}
+export interface ErrorCodeDestinationIpProhibited {
+ tag: 'destination-IP-prohibited',
+}
+export interface ErrorCodeDestinationIpUnroutable {
+ tag: 'destination-IP-unroutable',
+}
+export interface ErrorCodeConnectionRefused {
+ tag: 'connection-refused',
+}
+export interface ErrorCodeConnectionTerminated {
+ tag: 'connection-terminated',
+}
+export interface ErrorCodeConnectionTimeout {
+ tag: 'connection-timeout',
+}
+export interface ErrorCodeConnectionReadTimeout {
+ tag: 'connection-read-timeout',
+}
+export interface ErrorCodeConnectionWriteTimeout {
+ tag: 'connection-write-timeout',
+}
+export interface ErrorCodeConnectionLimitReached {
+ tag: 'connection-limit-reached',
+}
+export interface ErrorCodeTlsProtocolError {
+ tag: 'TLS-protocol-error',
+}
+export interface ErrorCodeTlsCertificateError {
+ tag: 'TLS-certificate-error',
+}
+export interface ErrorCodeTlsAlertReceived {
+ tag: 'TLS-alert-received',
+ val: TlsAlertReceivedPayload,
+}
+export interface ErrorCodeHttpRequestDenied {
+ tag: 'HTTP-request-denied',
+}
+export interface ErrorCodeHttpRequestLengthRequired {
+ tag: 'HTTP-request-length-required',
+}
+export interface ErrorCodeHttpRequestBodySize {
+ tag: 'HTTP-request-body-size',
+ val: bigint | undefined,
+}
+export interface ErrorCodeHttpRequestMethodInvalid {
+ tag: 'HTTP-request-method-invalid',
+}
+export interface ErrorCodeHttpRequestUriInvalid {
+ tag: 'HTTP-request-URI-invalid',
+}
+export interface ErrorCodeHttpRequestUriTooLong {
+ tag: 'HTTP-request-URI-too-long',
+}
+export interface ErrorCodeHttpRequestHeaderSectionSize {
+ tag: 'HTTP-request-header-section-size',
+ val: number | undefined,
+}
+export interface ErrorCodeHttpRequestHeaderSize {
+ tag: 'HTTP-request-header-size',
+ val: FieldSizePayload | undefined,
+}
+export interface ErrorCodeHttpRequestTrailerSectionSize {
+ tag: 'HTTP-request-trailer-section-size',
+ val: number | undefined,
+}
+export interface ErrorCodeHttpRequestTrailerSize {
+ tag: 'HTTP-request-trailer-size',
+ val: FieldSizePayload,
+}
+export interface ErrorCodeHttpResponseIncomplete {
+ tag: 'HTTP-response-incomplete',
+}
+export interface ErrorCodeHttpResponseHeaderSectionSize {
+ tag: 'HTTP-response-header-section-size',
+ val: number | undefined,
+}
+export interface ErrorCodeHttpResponseHeaderSize {
+ tag: 'HTTP-response-header-size',
+ val: FieldSizePayload,
+}
+export interface ErrorCodeHttpResponseBodySize {
+ tag: 'HTTP-response-body-size',
+ val: bigint | undefined,
+}
+export interface ErrorCodeHttpResponseTrailerSectionSize {
+ tag: 'HTTP-response-trailer-section-size',
+ val: number | undefined,
+}
+export interface ErrorCodeHttpResponseTrailerSize {
+ tag: 'HTTP-response-trailer-size',
+ val: FieldSizePayload,
+}
+export interface ErrorCodeHttpResponseTransferCoding {
+ tag: 'HTTP-response-transfer-coding',
+ val: string | undefined,
+}
+export interface ErrorCodeHttpResponseContentCoding {
+ tag: 'HTTP-response-content-coding',
+ val: string | undefined,
+}
+export interface ErrorCodeHttpResponseTimeout {
+ tag: 'HTTP-response-timeout',
+}
+export interface ErrorCodeHttpUpgradeFailed {
+ tag: 'HTTP-upgrade-failed',
+}
+export interface ErrorCodeHttpProtocolError {
+ tag: 'HTTP-protocol-error',
+}
+export interface ErrorCodeLoopDetected {
+ tag: 'loop-detected',
+}
+export interface ErrorCodeConfigurationError {
+ tag: 'configuration-error',
+}
+/**
+ * This is a catch-all error for anything that doesn't fit cleanly into a
+ * more specific case. It also includes an optional string for an
+ * unstructured description of the error. Users should not depend on the
+ * string for diagnosing errors, as it's not required to be consistent
+ * between implementations.
+ */
+export interface ErrorCodeInternalError {
+ tag: 'internal-error',
+ val: string | undefined,
+}
+/**
+ * This type enumerates the different kinds of errors that may occur when
+ * setting or appending to a `fields` resource.
+ */
+export type HeaderError = HeaderErrorInvalidSyntax | HeaderErrorForbidden | HeaderErrorImmutable;
+/**
+ * This error indicates that a `field-name` or `field-value` was
+ * syntactically invalid when used with an operation that sets headers in a
+ * `fields`.
+ */
+export interface HeaderErrorInvalidSyntax {
+ tag: 'invalid-syntax',
+}
+/**
+ * This error indicates that a forbidden `field-name` was used when trying
+ * to set a header in a `fields`.
+ */
+export interface HeaderErrorForbidden {
+ tag: 'forbidden',
+}
+/**
+ * This error indicates that the operation on the `fields` was not
+ * permitted because the fields are immutable.
+ */
+export interface HeaderErrorImmutable {
+ tag: 'immutable',
+}
+/**
+ * Field keys are always strings.
+ *
+ * Field keys should always be treated as case insensitive by the `fields`
+ * resource for the purposes of equality checking.
+ *
+ * # Deprecation
+ *
+ * This type has been deprecated in favor of the `field-name` type.
+ */
+export type FieldKey = string;
+/**
+ * Field names are always strings.
+ *
+ * Field names should always be treated as case insensitive by the `fields`
+ * resource for the purposes of equality checking.
+ */
+export type FieldName = FieldKey;
+/**
+ * Field values should always be ASCII strings. However, in
+ * reality, HTTP implementations often have to interpret malformed values,
+ * so they are provided as a list of bytes.
+ */
+export type FieldValue = Uint8Array;
+/**
+ * Headers is an alias for Fields.
+ */
+export type Headers = Fields;
+/**
+ * Trailers is an alias for Fields.
+ */
+export type Trailers = Fields;
+/**
+ * This type corresponds to the HTTP standard Status Code.
+ */
+export type StatusCode = number;
+export type Result = { tag: 'ok', val: T } | { tag: 'err', val: E };
+
+export class Fields {
+ /**
+ * Construct an empty HTTP Fields.
+ *
+ * The resulting `fields` is mutable.
+ */
+ constructor()
+ /**
+ * Construct an HTTP Fields.
+ *
+ * The resulting `fields` is mutable.
+ *
+ * The list represents each name-value pair in the Fields. Names
+ * which have multiple values are represented by multiple entries in this
+ * list with the same name.
+ *
+ * The tuple is a pair of the field name, represented as a string, and
+ * Value, represented as a list of bytes.
+ *
+ * An error result will be returned if any `field-name` or `field-value` is
+ * syntactically invalid, or if a field is forbidden.
+ */
+ static fromList(entries: Array<[FieldName, FieldValue]>): Fields;
+ /**
+ * Get all of the values corresponding to a name. If the name is not present
+ * in this `fields` or is syntactically invalid, an empty list is returned.
+ * However, if the name is present but empty, this is represented by a list
+ * with one or more empty field-values present.
+ */
+ get(name: FieldName): Array;
+ /**
+ * Returns `true` when the name is present in this `fields`. If the name is
+ * syntactically invalid, `false` is returned.
+ */
+ has(name: FieldName): boolean;
+ /**
+ * Set all of the values for a name. Clears any existing values for that
+ * name, if they have been set.
+ *
+ * Fails with `header-error.immutable` if the `fields` are immutable.
+ *
+ * Fails with `header-error.invalid-syntax` if the `field-name` or any of
+ * the `field-value`s are syntactically invalid.
+ */
+ set(name: FieldName, value: Array): void;
+ /**
+ * Delete all values for a name. Does nothing if no values for the name
+ * exist.
+ *
+ * Fails with `header-error.immutable` if the `fields` are immutable.
+ *
+ * Fails with `header-error.invalid-syntax` if the `field-name` is
+ * syntactically invalid.
+ */
+ 'delete'(name: FieldName): void;
+ /**
+ * Append a value for a name. Does not change or delete any existing
+ * values for that name.
+ *
+ * Fails with `header-error.immutable` if the `fields` are immutable.
+ *
+ * Fails with `header-error.invalid-syntax` if the `field-name` or
+ * `field-value` are syntactically invalid.
+ */
+ append(name: FieldName, value: FieldValue): void;
+ /**
+ * Retrieve the full set of names and values in the Fields. Like the
+ * constructor, the list represents each name-value pair.
+ *
+ * The outer list represents each name-value pair in the Fields. Names
+ * which have multiple values are represented by multiple entries in this
+ * list with the same name.
+ *
+ * The names and values are always returned in the original casing and in
+ * the order in which they will be serialized for transport.
+ */
+ entries(): Array<[FieldName, FieldValue]>;
+ /**
+ * Make a deep copy of the Fields. Equivalent in behavior to calling the
+ * `fields` constructor on the return value of `entries`. The resulting
+ * `fields` is mutable.
+ */
+ clone(): Fields;
+}
+
+export class FutureIncomingResponse {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns a pollable which becomes ready when either the Response has
+ * been received, or an error has occurred. When this pollable is ready,
+ * the `get` method will return `some`.
+ */
+ subscribe(): Pollable;
+ /**
+ * Returns the incoming HTTP Response, or an error, once one is ready.
+ *
+ * The outer `option` represents future readiness. Users can wait on this
+ * `option` to become `some` using the `subscribe` method.
+ *
+ * The outer `result` is used to retrieve the response or error at most
+ * once. It will be success on the first call in which the outer option
+ * is `some`, and error on subsequent calls.
+ *
+ * The inner `result` represents that either the incoming HTTP Response
+ * status and headers have received successfully, or that an error
+ * occurred. Errors may also occur while consuming the response body,
+ * but those will be reported by the `incoming-body` and its
+ * `output-stream` child.
+ */
+ get(): Result, void> | undefined;
+}
+
+export class FutureTrailers {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns a pollable which becomes ready when either the trailers have
+ * been received, or an error has occurred. When this pollable is ready,
+ * the `get` method will return `some`.
+ */
+ subscribe(): Pollable;
+ /**
+ * Returns the contents of the trailers, or an error which occurred,
+ * once the future is ready.
+ *
+ * The outer `option` represents future readiness. Users can wait on this
+ * `option` to become `some` using the `subscribe` method.
+ *
+ * The outer `result` is used to retrieve the trailers or error at most
+ * once. It will be success on the first call in which the outer option
+ * is `some`, and error on subsequent calls.
+ *
+ * The inner `result` represents that either the HTTP Request or Response
+ * body, as well as any trailers, were received successfully, or that an
+ * error occurred receiving them. The optional `trailers` indicates whether
+ * or not trailers were present in the body.
+ *
+ * When some `trailers` are returned by this method, the `trailers`
+ * resource is immutable, and a child. Use of the `set`, `append`, or
+ * `delete` methods will return an error, and the resource must be
+ * dropped before the parent `future-trailers` is dropped.
+ */
+ get(): Result, void> | undefined;
+}
+
+export class IncomingBody {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns the contents of the body, as a stream of bytes.
+ *
+ * Returns success on first call: the stream representing the contents
+ * can be retrieved at most once. Subsequent calls will return error.
+ *
+ * The returned `input-stream` resource is a child: it must be dropped
+ * before the parent `incoming-body` is dropped, or consumed by
+ * `incoming-body.finish`.
+ *
+ * This invariant ensures that the implementation can determine whether
+ * the user is consuming the contents of the body, waiting on the
+ * `future-trailers` to be ready, or neither. This allows for network
+ * backpressure is to be applied when the user is consuming the body,
+ * and for that backpressure to not inhibit delivery of the trailers if
+ * the user does not read the entire body.
+ */
+ stream(): InputStream;
+ /**
+ * Takes ownership of `incoming-body`, and returns a `future-trailers`.
+ * This function will trap if the `input-stream` child is still alive.
+ */
+ static finish(this_: IncomingBody): FutureTrailers;
+}
+
+export class IncomingRequest {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns the method of the incoming request.
+ */
+ method(): Method;
+ /**
+ * Returns the path with query parameters from the request, as a string.
+ */
+ pathWithQuery(): string | undefined;
+ /**
+ * Returns the protocol scheme from the request.
+ */
+ scheme(): Scheme | undefined;
+ /**
+ * Returns the authority of the Request's target URI, if present.
+ */
+ authority(): string | undefined;
+ /**
+ * Get the `headers` associated with the request.
+ *
+ * The returned `headers` resource is immutable: `set`, `append`, and
+ * `delete` operations will fail with `header-error.immutable`.
+ *
+ * The `headers` returned are a child resource: it must be dropped before
+ * the parent `incoming-request` is dropped. Dropping this
+ * `incoming-request` before all children are dropped will trap.
+ */
+ headers(): Headers;
+ /**
+ * Gives the `incoming-body` associated with this request. Will only
+ * return success at most once, and subsequent calls will return error.
+ */
+ consume(): IncomingBody;
+}
+
+export class IncomingResponse {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns the status code from the incoming response.
+ */
+ status(): StatusCode;
+ /**
+ * Returns the headers from the incoming response.
+ *
+ * The returned `headers` resource is immutable: `set`, `append`, and
+ * `delete` operations will fail with `header-error.immutable`.
+ *
+ * This headers resource is a child: it must be dropped before the parent
+ * `incoming-response` is dropped.
+ */
+ headers(): Headers;
+ /**
+ * Returns the incoming body. May be called at most once. Returns error
+ * if called additional times.
+ */
+ consume(): IncomingBody;
+}
+
+export class OutgoingBody {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns a stream for writing the body contents.
+ *
+ * The returned `output-stream` is a child resource: it must be dropped
+ * before the parent `outgoing-body` resource is dropped (or finished),
+ * otherwise the `outgoing-body` drop or `finish` will trap.
+ *
+ * Returns success on the first call: the `output-stream` resource for
+ * this `outgoing-body` may be retrieved at most once. Subsequent calls
+ * will return error.
+ */
+ write(): OutputStream;
+ /**
+ * Finalize an outgoing body, optionally providing trailers. This must be
+ * called to signal that the response is complete. If the `outgoing-body`
+ * is dropped without calling `outgoing-body.finalize`, the implementation
+ * should treat the body as corrupted.
+ *
+ * Fails if the body's `outgoing-request` or `outgoing-response` was
+ * constructed with a Content-Length header, and the contents written
+ * to the body (via `write`) does not match the value given in the
+ * Content-Length.
+ */
+ static finish(this_: OutgoingBody, trailers: Trailers | undefined): void;
+}
+
+export class OutgoingRequest {
+ /**
+ * Construct a new `outgoing-request` with a default `method` of `GET`, and
+ * `none` values for `path-with-query`, `scheme`, and `authority`.
+ *
+ * * `headers` is the HTTP Headers for the Request.
+ *
+ * It is possible to construct, or manipulate with the accessor functions
+ * below, an `outgoing-request` with an invalid combination of `scheme`
+ * and `authority`, or `headers` which are not permitted to be sent.
+ * It is the obligation of the `outgoing-handler.handle` implementation
+ * to reject invalid constructions of `outgoing-request`.
+ */
+ constructor(headers: Headers)
+ /**
+ * Returns the resource corresponding to the outgoing Body for this
+ * Request.
+ *
+ * Returns success on the first call: the `outgoing-body` resource for
+ * this `outgoing-request` can be retrieved at most once. Subsequent
+ * calls will return error.
+ */
+ body(): OutgoingBody;
+ /**
+ * Get the Method for the Request.
+ */
+ method(): Method;
+ /**
+ * Set the Method for the Request. Fails if the string present in a
+ * `method.other` argument is not a syntactically valid method.
+ */
+ setMethod(method: Method): void;
+ /**
+ * Get the combination of the HTTP Path and Query for the Request.
+ * When `none`, this represents an empty Path and empty Query.
+ */
+ pathWithQuery(): string | undefined;
+ /**
+ * Set the combination of the HTTP Path and Query for the Request.
+ * When `none`, this represents an empty Path and empty Query. Fails is the
+ * string given is not a syntactically valid path and query uri component.
+ */
+ setPathWithQuery(pathWithQuery: string | undefined): void;
+ /**
+ * Get the HTTP Related Scheme for the Request. When `none`, the
+ * implementation may choose an appropriate default scheme.
+ */
+ scheme(): Scheme | undefined;
+ /**
+ * Set the HTTP Related Scheme for the Request. When `none`, the
+ * implementation may choose an appropriate default scheme. Fails if the
+ * string given is not a syntactically valid uri scheme.
+ */
+ setScheme(scheme: Scheme | undefined): void;
+ /**
+ * Get the authority of the Request's target URI. A value of `none` may be used
+ * with Related Schemes which do not require an authority. The HTTP and
+ * HTTPS schemes always require an authority.
+ */
+ authority(): string | undefined;
+ /**
+ * Set the authority of the Request's target URI. A value of `none` may be used
+ * with Related Schemes which do not require an authority. The HTTP and
+ * HTTPS schemes always require an authority. Fails if the string given is
+ * not a syntactically valid URI authority.
+ */
+ setAuthority(authority: string | undefined): void;
+ /**
+ * Get the headers associated with the Request.
+ *
+ * The returned `headers` resource is immutable: `set`, `append`, and
+ * `delete` operations will fail with `header-error.immutable`.
+ *
+ * This headers resource is a child: it must be dropped before the parent
+ * `outgoing-request` is dropped, or its ownership is transferred to
+ * another component by e.g. `outgoing-handler.handle`.
+ */
+ headers(): Headers;
+}
+
+export class OutgoingResponse {
+ /**
+ * Construct an `outgoing-response`, with a default `status-code` of `200`.
+ * If a different `status-code` is needed, it must be set via the
+ * `set-status-code` method.
+ *
+ * * `headers` is the HTTP Headers for the Response.
+ */
+ constructor(headers: Headers)
+ /**
+ * Get the HTTP Status Code for the Response.
+ */
+ statusCode(): StatusCode;
+ /**
+ * Set the HTTP Status Code for the Response. Fails if the status-code
+ * given is not a valid http status code.
+ */
+ setStatusCode(statusCode: StatusCode): void;
+ /**
+ * Get the headers associated with the Request.
+ *
+ * The returned `headers` resource is immutable: `set`, `append`, and
+ * `delete` operations will fail with `header-error.immutable`.
+ *
+ * This headers resource is a child: it must be dropped before the parent
+ * `outgoing-request` is dropped, or its ownership is transferred to
+ * another component by e.g. `outgoing-handler.handle`.
+ */
+ headers(): Headers;
+ /**
+ * Returns the resource corresponding to the outgoing Body for this Response.
+ *
+ * Returns success on the first call: the `outgoing-body` resource for
+ * this `outgoing-response` can be retrieved at most once. Subsequent
+ * calls will return error.
+ */
+ body(): OutgoingBody;
+}
+
+export class RequestOptions {
+ /**
+ * Construct a default `request-options` value.
+ */
+ constructor()
+ /**
+ * The timeout for the initial connect to the HTTP Server.
+ */
+ connectTimeout(): Duration | undefined;
+ /**
+ * Set the timeout for the initial connect to the HTTP Server. An error
+ * return value indicates that this timeout is not supported.
+ */
+ setConnectTimeout(duration: Duration | undefined): void;
+ /**
+ * The timeout for receiving the first byte of the Response body.
+ */
+ firstByteTimeout(): Duration | undefined;
+ /**
+ * Set the timeout for receiving the first byte of the Response body. An
+ * error return value indicates that this timeout is not supported.
+ */
+ setFirstByteTimeout(duration: Duration | undefined): void;
+ /**
+ * The timeout for receiving subsequent chunks of bytes in the Response
+ * body stream.
+ */
+ betweenBytesTimeout(): Duration | undefined;
+ /**
+ * Set the timeout for receiving subsequent chunks of bytes in the Response
+ * body stream. An error return value indicates that this timeout is not
+ * supported.
+ */
+ setBetweenBytesTimeout(duration: Duration | undefined): void;
+}
+
+export class ResponseOutparam {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Set the value of the `response-outparam` to either send a response,
+ * or indicate an error.
+ *
+ * This method consumes the `response-outparam` to ensure that it is
+ * called at most once. If it is never called, the implementation
+ * will respond with an error.
+ *
+ * The user may provide an `error` to `response` to allow the
+ * implementation determine how to respond with an HTTP error response.
+ */
+ static set(param: ResponseOutparam, response: Result): void;
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-error.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-error.d.ts
new file mode 100644
index 0000000..bdd20f7
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-error.d.ts
@@ -0,0 +1,18 @@
+/** @module Interface wasi:io/error@0.2.3 **/
+
+export class Error {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns a string that is suitable to assist humans in debugging
+ * this error.
+ *
+ * WARNING: The returned string should not be consumed mechanically!
+ * It may change across platforms, hosts, or other implementation
+ * details. Parsing this string is a major platform-compatibility
+ * hazard.
+ */
+ toDebugString(): string;
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-poll.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-poll.d.ts
new file mode 100644
index 0000000..4b71df6
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-poll.d.ts
@@ -0,0 +1,44 @@
+/** @module Interface wasi:io/poll@0.2.3 **/
+/**
+ * Poll for completion on a set of pollables.
+ *
+ * This function takes a list of pollables, which identify I/O sources of
+ * interest, and waits until one or more of the events is ready for I/O.
+ *
+ * The result `list` contains one or more indices of handles in the
+ * argument list that is ready for I/O.
+ *
+ * This function traps if either:
+ * - the list is empty, or:
+ * - the list contains more elements than can be indexed with a `u32` value.
+ *
+ * A timeout can be implemented by adding a pollable from the
+ * wasi-clocks API to the list.
+ *
+ * This function does not return a `result`; polling in itself does not
+ * do any I/O so it doesn't fail. If any of the I/O sources identified by
+ * the pollables has an error, it is indicated by marking the source as
+ * being ready for I/O.
+ */
+export function poll(in_: Array): Uint32Array;
+
+export class Pollable {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Return the readiness of a pollable. This function never blocks.
+ *
+ * Returns `true` when the pollable is ready, and `false` otherwise.
+ */
+ ready(): boolean;
+ /**
+ * `block` returns immediately if the pollable is ready, and otherwise
+ * blocks until ready.
+ *
+ * This function is equivalent to calling `poll.poll` on a list
+ * containing only this pollable.
+ */
+ block(): void;
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-streams.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-streams.d.ts
new file mode 100644
index 0000000..0da6988
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-io-streams.d.ts
@@ -0,0 +1,243 @@
+/** @module Interface wasi:io/streams@0.2.3 **/
+export type Error = import('./wasi-io-error.js').Error;
+export type Pollable = import('./wasi-io-poll.js').Pollable;
+/**
+ * An error for input-stream and output-stream operations.
+ */
+export type StreamError = StreamErrorLastOperationFailed | StreamErrorClosed;
+/**
+ * The last operation (a write or flush) failed before completion.
+ *
+ * More information is available in the `error` payload.
+ *
+ * After this, the stream will be closed. All future operations return
+ * `stream-error::closed`.
+ */
+export interface StreamErrorLastOperationFailed {
+ tag: 'last-operation-failed',
+ val: Error,
+}
+/**
+ * The stream is closed: no more input will be accepted by the
+ * stream. A closed output-stream will return this error on all
+ * future operations.
+ */
+export interface StreamErrorClosed {
+ tag: 'closed',
+}
+
+export class InputStream {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Perform a non-blocking read from the stream.
+ *
+ * When the source of a `read` is binary data, the bytes from the source
+ * are returned verbatim. When the source of a `read` is known to the
+ * implementation to be text, bytes containing the UTF-8 encoding of the
+ * text are returned.
+ *
+ * This function returns a list of bytes containing the read data,
+ * when successful. The returned list will contain up to `len` bytes;
+ * it may return fewer than requested, but not more. The list is
+ * empty when no bytes are available for reading at this time. The
+ * pollable given by `subscribe` will be ready when more bytes are
+ * available.
+ *
+ * This function fails with a `stream-error` when the operation
+ * encounters an error, giving `last-operation-failed`, or when the
+ * stream is closed, giving `closed`.
+ *
+ * When the caller gives a `len` of 0, it represents a request to
+ * read 0 bytes. If the stream is still open, this call should
+ * succeed and return an empty list, or otherwise fail with `closed`.
+ *
+ * The `len` parameter is a `u64`, which could represent a list of u8 which
+ * is not possible to allocate in wasm32, or not desirable to allocate as
+ * as a return value by the callee. The callee may return a list of bytes
+ * less than `len` in size while more bytes are available for reading.
+ */
+ read(len: bigint): Uint8Array;
+ /**
+ * Read bytes from a stream, after blocking until at least one byte can
+ * be read. Except for blocking, behavior is identical to `read`.
+ */
+ blockingRead(len: bigint): Uint8Array;
+ /**
+ * Skip bytes from a stream. Returns number of bytes skipped.
+ *
+ * Behaves identical to `read`, except instead of returning a list
+ * of bytes, returns the number of bytes consumed from the stream.
+ */
+ skip(len: bigint): bigint;
+ /**
+ * Skip bytes from a stream, after blocking until at least one byte
+ * can be skipped. Except for blocking behavior, identical to `skip`.
+ */
+ blockingSkip(len: bigint): bigint;
+ /**
+ * Create a `pollable` which will resolve once either the specified stream
+ * has bytes available to read or the other end of the stream has been
+ * closed.
+ * The created `pollable` is a child resource of the `input-stream`.
+ * Implementations may trap if the `input-stream` is dropped before
+ * all derived `pollable`s created with this function are dropped.
+ */
+ subscribe(): Pollable;
+}
+
+export class OutputStream {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Check readiness for writing. This function never blocks.
+ *
+ * Returns the number of bytes permitted for the next call to `write`,
+ * or an error. Calling `write` with more bytes than this function has
+ * permitted will trap.
+ *
+ * When this function returns 0 bytes, the `subscribe` pollable will
+ * become ready when this function will report at least 1 byte, or an
+ * error.
+ */
+ checkWrite(): bigint;
+ /**
+ * Perform a write. This function never blocks.
+ *
+ * When the destination of a `write` is binary data, the bytes from
+ * `contents` are written verbatim. When the destination of a `write` is
+ * known to the implementation to be text, the bytes of `contents` are
+ * transcoded from UTF-8 into the encoding of the destination and then
+ * written.
+ *
+ * Precondition: check-write gave permit of Ok(n) and contents has a
+ * length of less than or equal to n. Otherwise, this function will trap.
+ *
+ * returns Err(closed) without writing if the stream has closed since
+ * the last call to check-write provided a permit.
+ */
+ write(contents: Uint8Array): void;
+ /**
+ * Perform a write of up to 4096 bytes, and then flush the stream. Block
+ * until all of these operations are complete, or an error occurs.
+ *
+ * This is a convenience wrapper around the use of `check-write`,
+ * `subscribe`, `write`, and `flush`, and is implemented with the
+ * following pseudo-code:
+ *
+ * ```text
+ * let pollable = this.subscribe();
+ * while !contents.is_empty() {
+ * // Wait for the stream to become writable
+ * pollable.block();
+ * let Ok(n) = this.check-write(); // eliding error handling
+ * let len = min(n, contents.len());
+ * let (chunk, rest) = contents.split_at(len);
+ * this.write(chunk ); // eliding error handling
+ * contents = rest;
+ * }
+ * this.flush();
+ * // Wait for completion of `flush`
+ * pollable.block();
+ * // Check for any errors that arose during `flush`
+ * let _ = this.check-write(); // eliding error handling
+ * ```
+ */
+ blockingWriteAndFlush(contents: Uint8Array): void;
+ /**
+ * Request to flush buffered output. This function never blocks.
+ *
+ * This tells the output-stream that the caller intends any buffered
+ * output to be flushed. the output which is expected to be flushed
+ * is all that has been passed to `write` prior to this call.
+ *
+ * Upon calling this function, the `output-stream` will not accept any
+ * writes (`check-write` will return `ok(0)`) until the flush has
+ * completed. The `subscribe` pollable will become ready when the
+ * flush has completed and the stream can accept more writes.
+ */
+ flush(): void;
+ /**
+ * Request to flush buffered output, and block until flush completes
+ * and stream is ready for writing again.
+ */
+ blockingFlush(): void;
+ /**
+ * Create a `pollable` which will resolve once the output-stream
+ * is ready for more writing, or an error has occurred. When this
+ * pollable is ready, `check-write` will return `ok(n)` with n>0, or an
+ * error.
+ *
+ * If the stream is closed, this pollable is always ready immediately.
+ *
+ * The created `pollable` is a child resource of the `output-stream`.
+ * Implementations may trap if the `output-stream` is dropped before
+ * all derived `pollable`s created with this function are dropped.
+ */
+ subscribe(): Pollable;
+ /**
+ * Write zeroes to a stream.
+ *
+ * This should be used precisely like `write` with the exact same
+ * preconditions (must use check-write first), but instead of
+ * passing a list of bytes, you simply pass the number of zero-bytes
+ * that should be written.
+ */
+ writeZeroes(len: bigint): void;
+ /**
+ * Perform a write of up to 4096 zeroes, and then flush the stream.
+ * Block until all of these operations are complete, or an error
+ * occurs.
+ *
+ * This is a convenience wrapper around the use of `check-write`,
+ * `subscribe`, `write-zeroes`, and `flush`, and is implemented with
+ * the following pseudo-code:
+ *
+ * ```text
+ * let pollable = this.subscribe();
+ * while num_zeroes != 0 {
+ * // Wait for the stream to become writable
+ * pollable.block();
+ * let Ok(n) = this.check-write(); // eliding error handling
+ * let len = min(n, num_zeroes);
+ * this.write-zeroes(len); // eliding error handling
+ * num_zeroes -= len;
+ * }
+ * this.flush();
+ * // Wait for completion of `flush`
+ * pollable.block();
+ * // Check for any errors that arose during `flush`
+ * let _ = this.check-write(); // eliding error handling
+ * ```
+ */
+ blockingWriteZeroesAndFlush(len: bigint): void;
+ /**
+ * Read from one stream and write to another.
+ *
+ * The behavior of splice is equivalent to:
+ * 1. calling `check-write` on the `output-stream`
+ * 2. calling `read` on the `input-stream` with the smaller of the
+ * `check-write` permitted length and the `len` provided to `splice`
+ * 3. calling `write` on the `output-stream` with that read data.
+ *
+ * Any error reported by the call to `check-write`, `read`, or
+ * `write` ends the splice and reports that error.
+ *
+ * This function returns the number of bytes transferred; it may be less
+ * than `len`.
+ */
+ splice(src: InputStream, len: bigint): bigint;
+ /**
+ * Read from one stream and write to another, with blocking.
+ *
+ * This is similar to `splice`, except that it blocks until the
+ * `output-stream` is ready for writing, and the `input-stream`
+ * is ready for reading, before performing the `splice`.
+ */
+ blockingSplice(src: InputStream, len: bigint): bigint;
+ }
+
\ No newline at end of file
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-insecure-seed.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-insecure-seed.d.ts
new file mode 100644
index 0000000..2a234cf
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-insecure-seed.d.ts
@@ -0,0 +1,21 @@
+/** @module Interface wasi:random/insecure-seed@0.2.3 **/
+/**
+ * Return a 128-bit value that may contain a pseudo-random value.
+ *
+ * The returned value is not required to be computed from a CSPRNG, and may
+ * even be entirely deterministic. Host implementations are encouraged to
+ * provide pseudo-random values to any program exposed to
+ * attacker-controlled content, to enable DoS protection built into many
+ * languages' hash-map implementations.
+ *
+ * This function is intended to only be called once, by a source language
+ * to initialize Denial Of Service (DoS) protection in its hash-map
+ * implementation.
+ *
+ * # Expected future evolution
+ *
+ * This will likely be changed to a value import, to prevent it from being
+ * called multiple times and potentially used for purposes other than DoS
+ * protection.
+ */
+export function insecureSeed(): [bigint, bigint];
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-insecure.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-insecure.d.ts
new file mode 100644
index 0000000..0af5f36
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-insecure.d.ts
@@ -0,0 +1,19 @@
+/** @module Interface wasi:random/insecure@0.2.3 **/
+/**
+ * Return `len` insecure pseudo-random bytes.
+ *
+ * This function is not cryptographically secure. Do not use it for
+ * anything related to security.
+ *
+ * There are no requirements on the values of the returned bytes, however
+ * implementations are encouraged to return evenly distributed values with
+ * a long period.
+ */
+export function getInsecureRandomBytes(len: bigint): Uint8Array;
+/**
+ * Return an insecure pseudo-random `u64` value.
+ *
+ * This function returns the same type of pseudo-random data as
+ * `get-insecure-random-bytes`, represented as a `u64`.
+ */
+export function getInsecureRandomU64(): bigint;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-random.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-random.d.ts
new file mode 100644
index 0000000..d19fc02
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-random-random.d.ts
@@ -0,0 +1,23 @@
+/** @module Interface wasi:random/random@0.2.3 **/
+/**
+ * Return `len` cryptographically-secure random or pseudo-random bytes.
+ *
+ * This function must produce data at least as cryptographically secure and
+ * fast as an adequately seeded cryptographically-secure pseudo-random
+ * number generator (CSPRNG). It must not block, from the perspective of
+ * the calling program, under any circumstances, including on the first
+ * request and on requests for numbers of bytes. The returned data must
+ * always be unpredictable.
+ *
+ * This function must always return fresh data. Deterministic environments
+ * must omit this function, rather than implementing it with deterministic
+ * data.
+ */
+export function getRandomBytes(len: bigint): Uint8Array;
+/**
+ * Return a cryptographically-secure random or pseudo-random `u64` value.
+ *
+ * This function returns the same type of data as `get-random-bytes`,
+ * represented as a `u64`.
+ */
+export function getRandomU64(): bigint;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-instance-network.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-instance-network.d.ts
new file mode 100644
index 0000000..22eea52
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-instance-network.d.ts
@@ -0,0 +1,6 @@
+/** @module Interface wasi:sockets/instance-network@0.2.3 **/
+/**
+ * Get a handle to the default network.
+ */
+export function instanceNetwork(): Network;
+export type Network = import('./wasi-sockets-network.js').Network;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-ip-name-lookup.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-ip-name-lookup.d.ts
new file mode 100644
index 0000000..7857d1a
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-ip-name-lookup.d.ts
@@ -0,0 +1,58 @@
+/** @module Interface wasi:sockets/ip-name-lookup@0.2.3 **/
+/**
+ * Resolve an internet host name to a list of IP addresses.
+ *
+ * Unicode domain names are automatically converted to ASCII using IDNA encoding.
+ * If the input is an IP address string, the address is parsed and returned
+ * as-is without making any external requests.
+ *
+ * See the wasi-socket proposal README.md for a comparison with getaddrinfo.
+ *
+ * This function never blocks. It either immediately fails or immediately
+ * returns successfully with a `resolve-address-stream` that can be used
+ * to (asynchronously) fetch the results.
+ *
+ * # Typical errors
+ * - `invalid-argument`: `name` is a syntactically invalid domain name or IP address.
+ *
+ * # References:
+ * -
+ * -
+ * -
+ * -
+ */
+export function resolveAddresses(network: Network, name: string): ResolveAddressStream;
+export type Pollable = import('./wasi-io-poll.js').Pollable;
+export type Network = import('./wasi-sockets-network.js').Network;
+export type ErrorCode = import('./wasi-sockets-network.js').ErrorCode;
+export type IpAddress = import('./wasi-sockets-network.js').IpAddress;
+
+export class ResolveAddressStream {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Returns the next address from the resolver.
+ *
+ * This function should be called multiple times. On each call, it will
+ * return the next address in connection order preference. If all
+ * addresses have been exhausted, this function returns `none`.
+ *
+ * This function never returns IPv4-mapped IPv6 addresses.
+ *
+ * # Typical errors
+ * - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY)
+ * - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN)
+ * - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL)
+ * - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN)
+ */
+ resolveNextAddress(): IpAddress | undefined;
+ /**
+ * Create a `pollable` which will resolve once the stream is ready for I/O.
+ *
+ * Note: this function is here for WASI 0.2 only.
+ * It's planned to be removed when `future` is natively supported in Preview3.
+ */
+ subscribe(): Pollable;
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-network.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-network.d.ts
new file mode 100644
index 0000000..be1cf60
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-network.d.ts
@@ -0,0 +1,164 @@
+/** @module Interface wasi:sockets/network@0.2.3 **/
+/**
+ * Error codes.
+ *
+ * In theory, every API can return any error code.
+ * In practice, API's typically only return the errors documented per API
+ * combined with a couple of errors that are always possible:
+ * - `unknown`
+ * - `access-denied`
+ * - `not-supported`
+ * - `out-of-memory`
+ * - `concurrency-conflict`
+ *
+ * See each individual API for what the POSIX equivalents are. They sometimes differ per API.
+ * # Variants
+ *
+ * ## `"unknown"`
+ *
+ * Unknown error
+ * ## `"access-denied"`
+ *
+ * Access denied.
+ *
+ * POSIX equivalent: EACCES, EPERM
+ * ## `"not-supported"`
+ *
+ * The operation is not supported.
+ *
+ * POSIX equivalent: EOPNOTSUPP
+ * ## `"invalid-argument"`
+ *
+ * One of the arguments is invalid.
+ *
+ * POSIX equivalent: EINVAL
+ * ## `"out-of-memory"`
+ *
+ * Not enough memory to complete the operation.
+ *
+ * POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY
+ * ## `"timeout"`
+ *
+ * The operation timed out before it could finish completely.
+ * ## `"concurrency-conflict"`
+ *
+ * This operation is incompatible with another asynchronous operation that is already in progress.
+ *
+ * POSIX equivalent: EALREADY
+ * ## `"not-in-progress"`
+ *
+ * Trying to finish an asynchronous operation that:
+ * - has not been started yet, or:
+ * - was already finished by a previous `finish-*` call.
+ *
+ * Note: this is scheduled to be removed when `future`s are natively supported.
+ * ## `"would-block"`
+ *
+ * The operation has been aborted because it could not be completed immediately.
+ *
+ * Note: this is scheduled to be removed when `future`s are natively supported.
+ * ## `"invalid-state"`
+ *
+ * The operation is not valid in the socket's current state.
+ * ## `"new-socket-limit"`
+ *
+ * A new socket resource could not be created because of a system limit.
+ * ## `"address-not-bindable"`
+ *
+ * A bind operation failed because the provided address is not an address that the `network` can bind to.
+ * ## `"address-in-use"`
+ *
+ * A bind operation failed because the provided address is already in use or because there are no ephemeral ports available.
+ * ## `"remote-unreachable"`
+ *
+ * The remote address is not reachable
+ * ## `"connection-refused"`
+ *
+ * The TCP connection was forcefully rejected
+ * ## `"connection-reset"`
+ *
+ * The TCP connection was reset.
+ * ## `"connection-aborted"`
+ *
+ * A TCP connection was aborted.
+ * ## `"datagram-too-large"`
+ *
+ * The size of a datagram sent to a UDP socket exceeded the maximum
+ * supported size.
+ * ## `"name-unresolvable"`
+ *
+ * Name does not exist or has no suitable associated IP addresses.
+ * ## `"temporary-resolver-failure"`
+ *
+ * A temporary failure in name resolution occurred.
+ * ## `"permanent-resolver-failure"`
+ *
+ * A permanent failure in name resolution occurred.
+ */
+export type ErrorCode = 'unknown' | 'access-denied' | 'not-supported' | 'invalid-argument' | 'out-of-memory' | 'timeout' | 'concurrency-conflict' | 'not-in-progress' | 'would-block' | 'invalid-state' | 'new-socket-limit' | 'address-not-bindable' | 'address-in-use' | 'remote-unreachable' | 'connection-refused' | 'connection-reset' | 'connection-aborted' | 'datagram-too-large' | 'name-unresolvable' | 'temporary-resolver-failure' | 'permanent-resolver-failure';
+/**
+ * # Variants
+ *
+ * ## `"ipv4"`
+ *
+ * Similar to `AF_INET` in POSIX.
+ * ## `"ipv6"`
+ *
+ * Similar to `AF_INET6` in POSIX.
+ */
+export type IpAddressFamily = 'ipv4' | 'ipv6';
+export type Ipv4Address = [number, number, number, number];
+export type Ipv6Address = [number, number, number, number, number, number, number, number];
+export type IpAddress = IpAddressIpv4 | IpAddressIpv6;
+export interface IpAddressIpv4 {
+ tag: 'ipv4',
+ val: Ipv4Address,
+}
+export interface IpAddressIpv6 {
+ tag: 'ipv6',
+ val: Ipv6Address,
+}
+export interface Ipv4SocketAddress {
+ /**
+ * sin_port
+ */
+ port: number,
+ /**
+ * sin_addr
+ */
+ address: Ipv4Address,
+}
+export interface Ipv6SocketAddress {
+ /**
+ * sin6_port
+ */
+ port: number,
+ /**
+ * sin6_flowinfo
+ */
+ flowInfo: number,
+ /**
+ * sin6_addr
+ */
+ address: Ipv6Address,
+ /**
+ * sin6_scope_id
+ */
+ scopeId: number,
+}
+export type IpSocketAddress = IpSocketAddressIpv4 | IpSocketAddressIpv6;
+export interface IpSocketAddressIpv4 {
+ tag: 'ipv4',
+ val: Ipv4SocketAddress,
+}
+export interface IpSocketAddressIpv6 {
+ tag: 'ipv6',
+ val: Ipv6SocketAddress,
+}
+
+export class Network {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-tcp-create-socket.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-tcp-create-socket.d.ts
new file mode 100644
index 0000000..73c11ab
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-tcp-create-socket.d.ts
@@ -0,0 +1,28 @@
+/** @module Interface wasi:sockets/tcp-create-socket@0.2.3 **/
+/**
+ * Create a new TCP socket.
+ *
+ * Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX.
+ * On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
+ *
+ * This function does not require a network capability handle. This is considered to be safe because
+ * at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect`
+ * is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+ *
+ * All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+ *
+ * # Typical errors
+ * - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT)
+ * - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+export function createTcpSocket(addressFamily: IpAddressFamily): TcpSocket;
+export type Network = import('./wasi-sockets-network.js').Network;
+export type ErrorCode = import('./wasi-sockets-network.js').ErrorCode;
+export type IpAddressFamily = import('./wasi-sockets-network.js').IpAddressFamily;
+export type TcpSocket = import('./wasi-sockets-tcp.js').TcpSocket;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-tcp.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-tcp.d.ts
new file mode 100644
index 0000000..7327807
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-tcp.d.ts
@@ -0,0 +1,359 @@
+/** @module Interface wasi:sockets/tcp@0.2.3 **/
+export type InputStream = import('./wasi-io-streams.js').InputStream;
+export type OutputStream = import('./wasi-io-streams.js').OutputStream;
+export type Pollable = import('./wasi-io-poll.js').Pollable;
+export type Duration = import('./wasi-clocks-monotonic-clock.js').Duration;
+export type Network = import('./wasi-sockets-network.js').Network;
+export type ErrorCode = import('./wasi-sockets-network.js').ErrorCode;
+export type IpSocketAddress = import('./wasi-sockets-network.js').IpSocketAddress;
+export type IpAddressFamily = import('./wasi-sockets-network.js').IpAddressFamily;
+/**
+ * # Variants
+ *
+ * ## `"receive"`
+ *
+ * Similar to `SHUT_RD` in POSIX.
+ * ## `"send"`
+ *
+ * Similar to `SHUT_WR` in POSIX.
+ * ## `"both"`
+ *
+ * Similar to `SHUT_RDWR` in POSIX.
+ */
+export type ShutdownType = 'receive' | 'send' | 'both';
+
+export class TcpSocket {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Bind the socket to a specific network on the provided IP address and port.
+ *
+ * If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which
+ * network interface(s) to bind to.
+ * If the TCP/UDP port is zero, the socket will be bound to a random free port.
+ *
+ * Bind can be attempted multiple times on the same socket, even with
+ * different arguments on each iteration. But never concurrently and
+ * only as long as the previous bind failed. Once a bind succeeds, the
+ * binding can't be changed anymore.
+ *
+ * # Typical errors
+ * - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
+ * - `invalid-argument`: `local-address` is not a unicast address. (EINVAL)
+ * - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL)
+ * - `invalid-state`: The socket is already bound. (EINVAL)
+ * - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
+ * - `address-in-use`: Address is already in use. (EADDRINUSE)
+ * - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL)
+ * - `not-in-progress`: A `bind` operation is not in progress.
+ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+ *
+ * # Implementors note
+ * When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT
+ * state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR
+ * socket option should be set implicitly on all platforms, except on Windows where this is the default behavior
+ * and SO_REUSEADDR performs something different entirely.
+ *
+ * Unlike in POSIX, in WASI the bind operation is async. This enables
+ * interactive WASI hosts to inject permission prompts. Runtimes that
+ * don't want to make use of this ability can simply call the native
+ * `bind` as part of either `start-bind` or `finish-bind`.
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ startBind(network: Network, localAddress: IpSocketAddress): void;
+ finishBind(): void;
+ /**
+ * Connect to a remote endpoint.
+ *
+ * On success:
+ * - the socket is transitioned into the `connected` state.
+ * - a pair of streams is returned that can be used to read & write to the connection
+ *
+ * After a failed connection attempt, the socket will be in the `closed`
+ * state and the only valid action left is to `drop` the socket. A single
+ * socket can not be used to connect more than once.
+ *
+ * # Typical errors
+ * - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+ * - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS)
+ * - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos)
+ * - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows)
+ * - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows)
+ * - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`.
+ * - `invalid-state`: The socket is already in the `connected` state. (EISCONN)
+ * - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows)
+ * - `timeout`: Connection timed out. (ETIMEDOUT)
+ * - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED)
+ * - `connection-reset`: The connection was reset. (ECONNRESET)
+ * - `connection-aborted`: The connection was aborted. (ECONNABORTED)
+ * - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+ * - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+ * - `not-in-progress`: A connect operation is not in progress.
+ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+ *
+ * # Implementors note
+ * The POSIX equivalent of `start-connect` is the regular `connect` syscall.
+ * Because all WASI sockets are non-blocking this is expected to return
+ * EINPROGRESS, which should be translated to `ok()` in WASI.
+ *
+ * The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT`
+ * with a timeout of 0 on the socket descriptor. Followed by a check for
+ * the `SO_ERROR` socket option, in case the poll signaled readiness.
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ startConnect(network: Network, remoteAddress: IpSocketAddress): void;
+ finishConnect(): [InputStream, OutputStream];
+ /**
+ * Start listening for new connections.
+ *
+ * Transitions the socket into the `listening` state.
+ *
+ * Unlike POSIX, the socket must already be explicitly bound.
+ *
+ * # Typical errors
+ * - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ)
+ * - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD)
+ * - `invalid-state`: The socket is already in the `listening` state.
+ * - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE)
+ * - `not-in-progress`: A listen operation is not in progress.
+ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+ *
+ * # Implementors note
+ * Unlike in POSIX, in WASI the listen operation is async. This enables
+ * interactive WASI hosts to inject permission prompts. Runtimes that
+ * don't want to make use of this ability can simply call the native
+ * `listen` as part of either `start-listen` or `finish-listen`.
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ startListen(): void;
+ finishListen(): void;
+ /**
+ * Accept a new client socket.
+ *
+ * The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket:
+ * - `address-family`
+ * - `keep-alive-enabled`
+ * - `keep-alive-idle-time`
+ * - `keep-alive-interval`
+ * - `keep-alive-count`
+ * - `hop-limit`
+ * - `receive-buffer-size`
+ * - `send-buffer-size`
+ *
+ * On success, this function returns the newly accepted client socket along with
+ * a pair of streams that can be used to read & write to the connection.
+ *
+ * # Typical errors
+ * - `invalid-state`: Socket is not in the `listening` state. (EINVAL)
+ * - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN)
+ * - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED)
+ * - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ accept(): [TcpSocket, InputStream, OutputStream];
+ /**
+ * Get the bound local address.
+ *
+ * POSIX mentions:
+ * > If the socket has not been bound to a local name, the value
+ * > stored in the object pointed to by `address` is unspecified.
+ *
+ * WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet.
+ *
+ * # Typical errors
+ * - `invalid-state`: The socket is not bound to any local address.
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ localAddress(): IpSocketAddress;
+ /**
+ * Get the remote address.
+ *
+ * # Typical errors
+ * - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ remoteAddress(): IpSocketAddress;
+ /**
+ * Whether the socket is in the `listening` state.
+ *
+ * Equivalent to the SO_ACCEPTCONN socket option.
+ */
+ isListening(): boolean;
+ /**
+ * Whether this is a IPv4 or IPv6 socket.
+ *
+ * Equivalent to the SO_DOMAIN socket option.
+ */
+ addressFamily(): IpAddressFamily;
+ /**
+ * Hints the desired listen queue size. Implementations are free to ignore this.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ * Any other value will never cause an error, but it might be silently clamped and/or rounded.
+ *
+ * # Typical errors
+ * - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen.
+ * - `invalid-argument`: (set) The provided value was 0.
+ * - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state.
+ */
+ setListenBacklogSize(value: bigint): void;
+ /**
+ * Enables or disables keepalive.
+ *
+ * The keepalive behavior can be adjusted using:
+ * - `keep-alive-idle-time`
+ * - `keep-alive-interval`
+ * - `keep-alive-count`
+ * These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true.
+ *
+ * Equivalent to the SO_KEEPALIVE socket option.
+ */
+ keepAliveEnabled(): boolean;
+ setKeepAliveEnabled(value: boolean): void;
+ /**
+ * Amount of time the connection has to be idle before TCP starts sending keepalive packets.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ * Any other value will never cause an error, but it might be silently clamped and/or rounded.
+ * I.e. after setting a value, reading the same setting back may return a different value.
+ *
+ * Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS)
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The provided value was 0.
+ */
+ keepAliveIdleTime(): Duration;
+ setKeepAliveIdleTime(value: Duration): void;
+ /**
+ * The time between keepalive packets.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ * Any other value will never cause an error, but it might be silently clamped and/or rounded.
+ * I.e. after setting a value, reading the same setting back may return a different value.
+ *
+ * Equivalent to the TCP_KEEPINTVL socket option.
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The provided value was 0.
+ */
+ keepAliveInterval(): Duration;
+ setKeepAliveInterval(value: Duration): void;
+ /**
+ * The maximum amount of keepalive packets TCP should send before aborting the connection.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ * Any other value will never cause an error, but it might be silently clamped and/or rounded.
+ * I.e. after setting a value, reading the same setting back may return a different value.
+ *
+ * Equivalent to the TCP_KEEPCNT socket option.
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The provided value was 0.
+ */
+ keepAliveCount(): number;
+ setKeepAliveCount(value: number): void;
+ /**
+ * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The TTL value must be 1 or higher.
+ */
+ hopLimit(): number;
+ setHopLimit(value: number): void;
+ /**
+ * The kernel buffer space reserved for sends/receives on this socket.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ * Any other value will never cause an error, but it might be silently clamped and/or rounded.
+ * I.e. after setting a value, reading the same setting back may return a different value.
+ *
+ * Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The provided value was 0.
+ */
+ receiveBufferSize(): bigint;
+ setReceiveBufferSize(value: bigint): void;
+ sendBufferSize(): bigint;
+ setSendBufferSize(value: bigint): void;
+ /**
+ * Create a `pollable` which can be used to poll for, or block on,
+ * completion of any of the asynchronous operations of this socket.
+ *
+ * When `finish-bind`, `finish-listen`, `finish-connect` or `accept`
+ * return `error(would-block)`, this pollable can be used to wait for
+ * their success or failure, after which the method can be retried.
+ *
+ * The pollable is not limited to the async operation that happens to be
+ * in progress at the time of calling `subscribe` (if any). Theoretically,
+ * `subscribe` only has to be called once per socket and can then be
+ * (re)used for the remainder of the socket's lifetime.
+ *
+ * See
+ * for more information.
+ *
+ * Note: this function is here for WASI 0.2 only.
+ * It's planned to be removed when `future` is natively supported in Preview3.
+ */
+ subscribe(): Pollable;
+ /**
+ * Initiate a graceful shutdown.
+ *
+ * - `receive`: The socket is not expecting to receive any data from
+ * the peer. The `input-stream` associated with this socket will be
+ * closed. Any data still in the receive queue at time of calling
+ * this method will be discarded.
+ * - `send`: The socket has no more data to send to the peer. The `output-stream`
+ * associated with this socket will be closed and a FIN packet will be sent.
+ * - `both`: Same effect as `receive` & `send` combined.
+ *
+ * This function is idempotent; shutting down a direction more than once
+ * has no effect and returns `ok`.
+ *
+ * The shutdown function does not close (drop) the socket.
+ *
+ * # Typical errors
+ * - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ shutdown(shutdownType: ShutdownType): void;
+}
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-udp-create-socket.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-udp-create-socket.d.ts
new file mode 100644
index 0000000..95d8ff7
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-udp-create-socket.d.ts
@@ -0,0 +1,28 @@
+/** @module Interface wasi:sockets/udp-create-socket@0.2.3 **/
+/**
+ * Create a new UDP socket.
+ *
+ * Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX.
+ * On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise.
+ *
+ * This function does not require a network capability handle. This is considered to be safe because
+ * at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called,
+ * the socket is effectively an in-memory configuration object, unable to communicate with the outside world.
+ *
+ * All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations.
+ *
+ * # Typical errors
+ * - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT)
+ * - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE)
+ *
+ * # References:
+ * -
+ * -
+ * -
+ * -
+ */
+export function createUdpSocket(addressFamily: IpAddressFamily): UdpSocket;
+export type Network = import('./wasi-sockets-network.js').Network;
+export type ErrorCode = import('./wasi-sockets-network.js').ErrorCode;
+export type IpAddressFamily = import('./wasi-sockets-network.js').IpAddressFamily;
+export type UdpSocket = import('./wasi-sockets-udp.js').UdpSocket;
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-udp.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-udp.d.ts
new file mode 100644
index 0000000..dc8ae48
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/interfaces/wasi-sockets-udp.d.ts
@@ -0,0 +1,301 @@
+/** @module Interface wasi:sockets/udp@0.2.3 **/
+export type Pollable = import('./wasi-io-poll.js').Pollable;
+export type Network = import('./wasi-sockets-network.js').Network;
+export type ErrorCode = import('./wasi-sockets-network.js').ErrorCode;
+export type IpSocketAddress = import('./wasi-sockets-network.js').IpSocketAddress;
+export type IpAddressFamily = import('./wasi-sockets-network.js').IpAddressFamily;
+/**
+ * A received datagram.
+ */
+export interface IncomingDatagram {
+ /**
+ * The payload.
+ *
+ * Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes.
+ */
+ data: Uint8Array,
+ /**
+ * The source address.
+ *
+ * This field is guaranteed to match the remote address the stream was initialized with, if any.
+ *
+ * Equivalent to the `src_addr` out parameter of `recvfrom`.
+ */
+ remoteAddress: IpSocketAddress,
+}
+/**
+ * A datagram to be sent out.
+ */
+export interface OutgoingDatagram {
+ /**
+ * The payload.
+ */
+ data: Uint8Array,
+ /**
+ * The destination address.
+ *
+ * The requirements on this field depend on how the stream was initialized:
+ * - with a remote address: this field must be None or match the stream's remote address exactly.
+ * - without a remote address: this field is required.
+ *
+ * If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`.
+ */
+ remoteAddress?: IpSocketAddress,
+}
+
+export class IncomingDatagramStream {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Receive messages on the socket.
+ *
+ * This function attempts to receive up to `max-results` datagrams on the socket without blocking.
+ * The returned list may contain fewer elements than requested, but never more.
+ *
+ * This function returns successfully with an empty list when either:
+ * - `max-results` is 0, or:
+ * - `max-results` is greater than 0, but no results are immediately available.
+ * This function never returns `error(would-block)`.
+ *
+ * # Typical errors
+ * - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+ * - `connection-refused`: The connection was refused. (ECONNREFUSED)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ * -
+ * -
+ * -
+ * -
+ */
+ receive(maxResults: bigint): Array;
+ /**
+ * Create a `pollable` which will resolve once the stream is ready to receive again.
+ *
+ * Note: this function is here for WASI 0.2 only.
+ * It's planned to be removed when `future` is natively supported in Preview3.
+ */
+ subscribe(): Pollable;
+}
+
+export class OutgoingDatagramStream {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Check readiness for sending. This function never blocks.
+ *
+ * Returns the number of datagrams permitted for the next call to `send`,
+ * or an error. Calling `send` with more datagrams than this function has
+ * permitted will trap.
+ *
+ * When this function returns ok(0), the `subscribe` pollable will
+ * become ready when this function will report at least ok(1), or an
+ * error.
+ *
+ * Never returns `would-block`.
+ */
+ checkSend(): bigint;
+ /**
+ * Send messages on the socket.
+ *
+ * This function attempts to send all provided `datagrams` on the socket without blocking and
+ * returns how many messages were actually sent (or queued for sending). This function never
+ * returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned.
+ *
+ * This function semantically behaves the same as iterating the `datagrams` list and sequentially
+ * sending each individual datagram until either the end of the list has been reached or the first error occurred.
+ * If at least one datagram has been sent successfully, this function never returns an error.
+ *
+ * If the input list is empty, the function returns `ok(0)`.
+ *
+ * Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if
+ * either `check-send` was not called or `datagrams` contains more items than `check-send` permitted.
+ *
+ * # Typical errors
+ * - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+ * - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
+ * - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
+ * - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN)
+ * - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ)
+ * - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+ * - `connection-refused`: The connection was refused. (ECONNREFUSED)
+ * - `datagram-too-large`: The datagram is too large. (EMSGSIZE)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ * -
+ * -
+ * -
+ * -
+ */
+ send(datagrams: Array): bigint;
+ /**
+ * Create a `pollable` which will resolve once the stream is ready to send again.
+ *
+ * Note: this function is here for WASI 0.2 only.
+ * It's planned to be removed when `future` is natively supported in Preview3.
+ */
+ subscribe(): Pollable;
+}
+
+export class UdpSocket {
+ /**
+ * This type does not have a public constructor.
+ */
+ private constructor();
+ /**
+ * Bind the socket to a specific network on the provided IP address and port.
+ *
+ * If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which
+ * network interface(s) to bind to.
+ * If the port is zero, the socket will be bound to a random free port.
+ *
+ * # Typical errors
+ * - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows)
+ * - `invalid-state`: The socket is already bound. (EINVAL)
+ * - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows)
+ * - `address-in-use`: Address is already in use. (EADDRINUSE)
+ * - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL)
+ * - `not-in-progress`: A `bind` operation is not in progress.
+ * - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN)
+ *
+ * # Implementors note
+ * Unlike in POSIX, in WASI the bind operation is async. This enables
+ * interactive WASI hosts to inject permission prompts. Runtimes that
+ * don't want to make use of this ability can simply call the native
+ * `bind` as part of either `start-bind` or `finish-bind`.
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ startBind(network: Network, localAddress: IpSocketAddress): void;
+ finishBind(): void;
+ /**
+ * Set up inbound & outbound communication channels, optionally to a specific peer.
+ *
+ * This function only changes the local socket configuration and does not generate any network traffic.
+ * On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well,
+ * based on the best network path to `remote-address`.
+ *
+ * When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer:
+ * - `send` can only be used to send to this destination.
+ * - `receive` will only return datagrams sent from the provided `remote-address`.
+ *
+ * This method may be called multiple times on the same socket to change its association, but
+ * only the most recently returned pair of streams will be operational. Implementations may trap if
+ * the streams returned by a previous invocation haven't been dropped yet before calling `stream` again.
+ *
+ * The POSIX equivalent in pseudo-code is:
+ * ```text
+ * if (was previously connected) {
+ * connect(s, AF_UNSPEC)
+ * }
+ * if (remote_address is Some) {
+ * connect(s, remote_address)
+ * }
+ * ```
+ *
+ * Unlike in POSIX, the socket must already be explicitly bound.
+ *
+ * # Typical errors
+ * - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT)
+ * - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL)
+ * - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL)
+ * - `invalid-state`: The socket is not bound.
+ * - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD)
+ * - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET)
+ * - `connection-refused`: The connection was refused. (ECONNREFUSED)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ stream(remoteAddress: IpSocketAddress | undefined): [IncomingDatagramStream, OutgoingDatagramStream];
+ /**
+ * Get the current bound address.
+ *
+ * POSIX mentions:
+ * > If the socket has not been bound to a local name, the value
+ * > stored in the object pointed to by `address` is unspecified.
+ *
+ * WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet.
+ *
+ * # Typical errors
+ * - `invalid-state`: The socket is not bound to any local address.
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ localAddress(): IpSocketAddress;
+ /**
+ * Get the address the socket is currently streaming to.
+ *
+ * # Typical errors
+ * - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN)
+ *
+ * # References
+ * -
+ * -
+ * -
+ * -
+ */
+ remoteAddress(): IpSocketAddress;
+ /**
+ * Whether this is a IPv4 or IPv6 socket.
+ *
+ * Equivalent to the SO_DOMAIN socket option.
+ */
+ addressFamily(): IpAddressFamily;
+ /**
+ * Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The TTL value must be 1 or higher.
+ */
+ unicastHopLimit(): number;
+ setUnicastHopLimit(value: number): void;
+ /**
+ * The kernel buffer space reserved for sends/receives on this socket.
+ *
+ * If the provided value is 0, an `invalid-argument` error is returned.
+ * Any other value will never cause an error, but it might be silently clamped and/or rounded.
+ * I.e. after setting a value, reading the same setting back may return a different value.
+ *
+ * Equivalent to the SO_RCVBUF and SO_SNDBUF socket options.
+ *
+ * # Typical errors
+ * - `invalid-argument`: (set) The provided value was 0.
+ */
+ receiveBufferSize(): bigint;
+ setReceiveBufferSize(value: bigint): void;
+ sendBufferSize(): bigint;
+ setSendBufferSize(value: bigint): void;
+ /**
+ * Create a `pollable` which will resolve once the socket is ready for I/O.
+ *
+ * Note: this function is here for WASI 0.2 only.
+ * It's planned to be removed when `future` is natively supported in Preview3.
+ */
+ subscribe(): Pollable;
+ }
+
\ No newline at end of file
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/io.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/io.d.ts
new file mode 100644
index 0000000..7a9b351
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/io.d.ts
@@ -0,0 +1,3 @@
+export type * as error from './interfaces/wasi-io-error.d.ts';
+export type * as poll from './interfaces/wasi-io-poll.d.ts';
+export type * as streams from './interfaces/wasi-io-streams.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/random.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/random.d.ts
new file mode 100644
index 0000000..cffbc45
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/random.d.ts
@@ -0,0 +1,3 @@
+export type * as insecureSeed from './interfaces/wasi-random-insecure-seed.d.ts';
+export type * as insecure from './interfaces/wasi-random-insecure.d.ts';
+export type * as random from './interfaces/wasi-random-random.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/sockets.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/sockets.d.ts
new file mode 100644
index 0000000..d0a9807
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/sockets.d.ts
@@ -0,0 +1,7 @@
+export type * as instanceNetwork from './interfaces/wasi-sockets-instance-network.d.ts';
+export type * as ipNameLookup from './interfaces/wasi-sockets-ip-name-lookup.d.ts';
+export type * as network from './interfaces/wasi-sockets-network.d.ts';
+export type * as tcpCreateSocket from './interfaces/wasi-sockets-tcp-create-socket.d.ts';
+export type * as tcp from './interfaces/wasi-sockets-tcp.d.ts';
+export type * as udpCreateSocket from './interfaces/wasi-sockets-udp-create-socket.d.ts';
+export type * as udp from './interfaces/wasi-sockets-udp.d.ts';
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/wasi-cli-command.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/wasi-cli-command.d.ts
new file mode 100644
index 0000000..1628ee2
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/wasi-cli-command.d.ts
@@ -0,0 +1,29 @@
+// world wasi:cli/command@0.2.3
+export type * as WasiCliEnvironment023 from './interfaces/wasi-cli-environment.js'; // import wasi:cli/environment@0.2.3
+export type * as WasiCliExit023 from './interfaces/wasi-cli-exit.js'; // import wasi:cli/exit@0.2.3
+export type * as WasiCliStderr023 from './interfaces/wasi-cli-stderr.js'; // import wasi:cli/stderr@0.2.3
+export type * as WasiCliStdin023 from './interfaces/wasi-cli-stdin.js'; // import wasi:cli/stdin@0.2.3
+export type * as WasiCliStdout023 from './interfaces/wasi-cli-stdout.js'; // import wasi:cli/stdout@0.2.3
+export type * as WasiCliTerminalInput023 from './interfaces/wasi-cli-terminal-input.js'; // import wasi:cli/terminal-input@0.2.3
+export type * as WasiCliTerminalOutput023 from './interfaces/wasi-cli-terminal-output.js'; // import wasi:cli/terminal-output@0.2.3
+export type * as WasiCliTerminalStderr023 from './interfaces/wasi-cli-terminal-stderr.js'; // import wasi:cli/terminal-stderr@0.2.3
+export type * as WasiCliTerminalStdin023 from './interfaces/wasi-cli-terminal-stdin.js'; // import wasi:cli/terminal-stdin@0.2.3
+export type * as WasiCliTerminalStdout023 from './interfaces/wasi-cli-terminal-stdout.js'; // import wasi:cli/terminal-stdout@0.2.3
+export type * as WasiClocksMonotonicClock023 from './interfaces/wasi-clocks-monotonic-clock.js'; // import wasi:clocks/monotonic-clock@0.2.3
+export type * as WasiClocksWallClock023 from './interfaces/wasi-clocks-wall-clock.js'; // import wasi:clocks/wall-clock@0.2.3
+export type * as WasiFilesystemPreopens023 from './interfaces/wasi-filesystem-preopens.js'; // import wasi:filesystem/preopens@0.2.3
+export type * as WasiFilesystemTypes023 from './interfaces/wasi-filesystem-types.js'; // import wasi:filesystem/types@0.2.3
+export type * as WasiIoError023 from './interfaces/wasi-io-error.js'; // import wasi:io/error@0.2.3
+export type * as WasiIoPoll023 from './interfaces/wasi-io-poll.js'; // import wasi:io/poll@0.2.3
+export type * as WasiIoStreams023 from './interfaces/wasi-io-streams.js'; // import wasi:io/streams@0.2.3
+export type * as WasiRandomInsecureSeed023 from './interfaces/wasi-random-insecure-seed.js'; // import wasi:random/insecure-seed@0.2.3
+export type * as WasiRandomInsecure023 from './interfaces/wasi-random-insecure.js'; // import wasi:random/insecure@0.2.3
+export type * as WasiRandomRandom023 from './interfaces/wasi-random-random.js'; // import wasi:random/random@0.2.3
+export type * as WasiSocketsInstanceNetwork023 from './interfaces/wasi-sockets-instance-network.js'; // import wasi:sockets/instance-network@0.2.3
+export type * as WasiSocketsIpNameLookup023 from './interfaces/wasi-sockets-ip-name-lookup.js'; // import wasi:sockets/ip-name-lookup@0.2.3
+export type * as WasiSocketsNetwork023 from './interfaces/wasi-sockets-network.js'; // import wasi:sockets/network@0.2.3
+export type * as WasiSocketsTcpCreateSocket023 from './interfaces/wasi-sockets-tcp-create-socket.js'; // import wasi:sockets/tcp-create-socket@0.2.3
+export type * as WasiSocketsTcp023 from './interfaces/wasi-sockets-tcp.js'; // import wasi:sockets/tcp@0.2.3
+export type * as WasiSocketsUdpCreateSocket023 from './interfaces/wasi-sockets-udp-create-socket.js'; // import wasi:sockets/udp-create-socket@0.2.3
+export type * as WasiSocketsUdp023 from './interfaces/wasi-sockets-udp.js'; // import wasi:sockets/udp@0.2.3
+export * as run from './interfaces/wasi-cli-run.js'; // export wasi:cli/run@0.2.3
diff --git a/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/wasi-http-proxy.d.ts b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/wasi-http-proxy.d.ts
new file mode 100644
index 0000000..e9d49aa
--- /dev/null
+++ b/packages/web-host/overrides/@bytecodealliance/preview2-shim/types/wasi-http-proxy.d.ts
@@ -0,0 +1,13 @@
+// world wasi:http/proxy@0.2.3
+export type * as WasiCliStderr023 from './interfaces/wasi-cli-stderr.js'; // import wasi:cli/stderr@0.2.3
+export type * as WasiCliStdin023 from './interfaces/wasi-cli-stdin.js'; // import wasi:cli/stdin@0.2.3
+export type * as WasiCliStdout023 from './interfaces/wasi-cli-stdout.js'; // import wasi:cli/stdout@0.2.3
+export type * as WasiClocksMonotonicClock023 from './interfaces/wasi-clocks-monotonic-clock.js'; // import wasi:clocks/monotonic-clock@0.2.3
+export type * as WasiClocksWallClock023 from './interfaces/wasi-clocks-wall-clock.js'; // import wasi:clocks/wall-clock@0.2.3
+export type * as WasiHttpOutgoingHandler023 from './interfaces/wasi-http-outgoing-handler.js'; // import wasi:http/outgoing-handler@0.2.3
+export type * as WasiHttpTypes023 from './interfaces/wasi-http-types.js'; // import wasi:http/types@0.2.3
+export type * as WasiIoError023 from './interfaces/wasi-io-error.js'; // import wasi:io/error@0.2.3
+export type * as WasiIoPoll023 from './interfaces/wasi-io-poll.js'; // import wasi:io/poll@0.2.3
+export type * as WasiIoStreams023 from './interfaces/wasi-io-streams.js'; // import wasi:io/streams@0.2.3
+export type * as WasiRandomRandom023 from './interfaces/wasi-random-random.js'; // import wasi:random/random@0.2.3
+export * as incomingHandler from './interfaces/wasi-http-incoming-handler.js'; // export wasi:http/incoming-handler@0.2.3
diff --git a/packages/web-host/src/components/HomePage.tsx b/packages/web-host/src/components/HomePage.tsx
index 61218dd..c49596d 100644
--- a/packages/web-host/src/components/HomePage.tsx
+++ b/packages/web-host/src/components/HomePage.tsx
@@ -60,10 +60,10 @@ export const HomePage = ({ onStartRepl }: HomePageProps) => {
runs in browsers and terminals with different host implementations.
- Plugins like cat
or ls
can interact with a
- virtual filesystem using the primitives of the languages they are
- written in (like std::fs::read_dir
or{" "}
- std::fs::read_to_string
).
+ Plugins like cat
, ls
or tee
can
+ interact with a virtual filesystem using the primitives of the
+ languages they are written in (like std::fs::read_dir
,{" "}
+ std::fs::read_to_string
or std::fs::write
).
Key Features
@@ -75,9 +75,10 @@ export const HomePage = ({ onStartRepl }: HomePageProps) => {
Language-agnostic plugins (any language that compiles to WASM)
- Virtual filesystem: the I/O operations are forwarded via the{" "}
- @bytecodealliance/preview2-shim/filesystem
shim, which
- shims wasi:filesystem
+ Virtual filesystem: the I/O operations are forwarded via a local
+ fork of @bytecodealliance/preview2-shim/filesystem
,
+ which shims wasi:filesystem
(yes, you can do{" "}
+ WRITE operations)
Modern React + TypeScript web interface
diff --git a/packages/web-host/src/components/ReplHistory.tsx b/packages/web-host/src/components/ReplHistory.tsx
index fd202a9..922cf7b 100644
--- a/packages/web-host/src/components/ReplHistory.tsx
+++ b/packages/web-host/src/components/ReplHistory.tsx
@@ -23,6 +23,7 @@ export function ReplHistory({
{entry.stdin}
@@ -31,6 +32,7 @@ export function ReplHistory({
@@ -40,6 +42,7 @@ export function ReplHistory({
className="bg-green-100 whitespace-pre-wrap before:content-[attr(data-status)] relative before:absolute before:right-0 before:top-0 word-break-word"
data-status={entry.status === "success" ? "✅" : "❌"}
data-stdtype="stdout"
+ data-std-index={index}
>
{entry.stdout}
@@ -49,6 +52,7 @@ export function ReplHistory({
className="bg-red-100 whitespace-pre-wrap before:content-[attr(data-status)] relative before:absolute before:right-0 before:top-0 word-break-word"
data-status={entry.status === "success" ? "✅" : "❌"}
data-stdtype="stderr"
+ data-std-index={index}
>
{entry.stderr}
diff --git a/packages/web-host/src/hooks/exampleCommands.ts b/packages/web-host/src/hooks/exampleCommands.ts
index e663820..062c392 100644
--- a/packages/web-host/src/hooks/exampleCommands.ts
+++ b/packages/web-host/src/hooks/exampleCommands.ts
@@ -41,6 +41,16 @@ const commands = [
() => ls("data/processed/2024"),
() => ls("documents"),
() => cat("documents/config.json"),
+ "echo We can also write to files! 🔥",
+ "tee output.txt",
+ "cat output.txt",
+ "echo We can write to files in append mode!",
+ "tee -a output.txt",
+ "echo Some more text",
+ "tee -a output.txt",
+ "echo Even more text",
+ "tee -a output.txt",
+ "cat output.txt",
"weather Paris",
"man weather",
"help",
diff --git a/packages/web-host/src/utils/github.ts b/packages/web-host/src/utils/github.ts
index a0b3038..cec7916 100644
--- a/packages/web-host/src/utils/github.ts
+++ b/packages/web-host/src/utils/github.ts
@@ -8,6 +8,7 @@ const pluginSourceUrlMapping = {
cat: "https://github.com/topheman/webassembly-component-model-experiments/tree/master/crates/plugin-cat",
echoc:
"https://github.com/topheman/webassembly-component-model-experiments/blob/master/c_modules/plugin-echo/component.c",
+ tee: "https://github.com/topheman/webassembly-component-model-experiments/tree/master/crates/plugin-tee",
} as const;
export function getPluginSourceUrl(
diff --git a/packages/web-host/src/wasm/engine.ts b/packages/web-host/src/wasm/engine.ts
index 0768171..f9a7fee 100644
--- a/packages/web-host/src/wasm/engine.ts
+++ b/packages/web-host/src/wasm/engine.ts
@@ -41,6 +41,7 @@ async function loadPlugins({
import("./generated/plugin_greet/transpiled/plugin_greet.js"),
import("./generated/plugin_ls/transpiled/plugin_ls.js"),
import("./generated/plugin_cat/transpiled/plugin_cat.js"),
+ import("./generated/plugin_tee/transpiled/plugin_tee.js"),
import("./generated/plugin-echo-c/transpiled/plugin-echo-c.js"),
]).then((plugins) =>
plugins.map((plugin) => {
diff --git a/packages/web-host/tests/repl-loading.spec.ts b/packages/web-host/tests/repl-loading.spec.ts
index aff40bf..46f0dc1 100644
--- a/packages/web-host/tests/repl-loading.spec.ts
+++ b/packages/web-host/tests/repl-loading.spec.ts
@@ -11,7 +11,7 @@ test("repl logic should have loaded", async ({ page }) => {
});
test("plugins should have loaded under their names", async ({ page }) => {
- const pluginNames = ["echo", "weather", "greet", "ls", "cat", "echoc"];
+ const pluginNames = ["echo", "weather", "greet", "ls", "cat", "echoc", "tee"];
await page.goto("/#repl");
for (const pluginName of pluginNames) {
await expect(
diff --git a/packages/web-host/tests/repl-logic.spec.ts b/packages/web-host/tests/repl-logic.spec.ts
index 8b72350..2e43f45 100644
--- a/packages/web-host/tests/repl-logic.spec.ts
+++ b/packages/web-host/tests/repl-logic.spec.ts
@@ -80,6 +80,7 @@ help reserved
list-commands reserved
ls plugin
man reserved
+tee plugin
weather plugin`,
);
});
diff --git a/packages/web-host/tests/repl-plugins.spec.ts b/packages/web-host/tests/repl-plugins.spec.ts
index 084bc75..0e7355c 100644
--- a/packages/web-host/tests/repl-plugins.spec.ts
+++ b/packages/web-host/tests/repl-plugins.spec.ts
@@ -1,5 +1,5 @@
import { expect, test } from "@playwright/test";
-import { fillAndSubmitCommand, getLastStd } from "./utils";
+import { fillAndSubmitCommand, getLastStd, sleep } from "./utils";
test("echo foo", async ({ page }) => {
await page.goto("/#repl");
@@ -89,3 +89,53 @@ test("cat README.md", async ({ page }) => {
You are interacting with a virtual filesystem, in your browser!`);
});
+
+test("tee new-file.txt", async ({ page }) => {
+ await page.goto("/#repl");
+ // check the file don't exist
+ await fillAndSubmitCommand(page, "ls", {
+ expectStdout: `D data
+D documents
+D logs
+F .config
+F .hidden_file
+F README.md`,
+ });
+ await fillAndSubmitCommand(page, "echo Some Content");
+ await sleep();
+ await fillAndSubmitCommand(page, "tee new-file.txt", {
+ expectStdout: "Some Content",
+ });
+ await fillAndSubmitCommand(page, "cat new-file.txt", {
+ expectStdout: "Some Content",
+ });
+});
+
+test("tee README.md", async ({ page }) => {
+ await page.goto("/#repl");
+ await fillAndSubmitCommand(page, "echo Some Content");
+ await sleep();
+ await fillAndSubmitCommand(page, "tee README.md", {
+ expectStdout: "Some Content",
+ });
+ await fillAndSubmitCommand(page, "cat README.md", {
+ expectStdout: "Some Content",
+ });
+});
+
+test("tee -a output.txt", async ({ page }) => {
+ await page.goto("/#repl");
+ await fillAndSubmitCommand(page, "echo Some Initial Content");
+ await sleep();
+ await fillAndSubmitCommand(page, "tee output.txt", {
+ expectStdout: "Some Initial Content",
+ });
+ await fillAndSubmitCommand(page, "echo Some More Content");
+ await sleep();
+ await fillAndSubmitCommand(page, "tee -a output.txt", {
+ expectStdout: "Some More Content",
+ });
+ await fillAndSubmitCommand(page, "cat output.txt", {
+ expectStdout: "Some Initial Content\nSome More Content",
+ });
+});
diff --git a/packages/web-host/tests/utils.ts b/packages/web-host/tests/utils.ts
index 78409b6..551bfce 100644
--- a/packages/web-host/tests/utils.ts
+++ b/packages/web-host/tests/utils.ts
@@ -1,13 +1,50 @@
-import { expect, type Page } from "@playwright/test";
+import { expect, type Locator, type Page } from "@playwright/test";
/**
* Get the last std output of the given type
+ * If expectContent is provided, it will retry to get the last std output until it matches the expected content
*/
export async function getLastStd(
page: Page,
type: "stdin" | "stdout" | "stderr",
+ {
+ expectContent,
+ }: {
+ expectContent?: string;
+ } = {},
) {
- return await page.locator(`[data-stdtype='${type}']`).last();
+ const locator = await page.locator(`[data-stdtype='${type}']`).last();
+ if (expectContent) {
+ const text = await locator.textContent();
+ if (text?.includes(expectContent)) {
+ return locator;
+ }
+ // if no match, do a hard expect that will fail the test with a clear error message
+ // Sorry you landed here, you will most likely have to add some `sleep()` in your code 🥲
+ await expect(locator).toHaveText(expectContent);
+ }
+ return locator;
+}
+
+/**
+ * Get the last std output of the given type after the given locator
+ * This is useful to get the last std output after a command has been submitted
+ * This ensures you don't have false positives when checking the last std output
+ */
+export async function getLastStdAfter(
+ page: Page,
+ type: "stdin" | "stdout" | "stderr",
+ stdLocator: Locator,
+) {
+ const stdinIndex = await stdLocator.getAttribute("data-std-index");
+ return await page
+ .locator(`[data-std-index='${stdinIndex}'] ~ [data-stdtype='${type}']`)
+ .last();
+}
+
+export async function sleep(ms?: number): Promise {
+ const DEFAULT_DELAY = 200; // taking into account the default delay necessary in the CI
+ return new Promise((resolve) => setTimeout(resolve, ms ?? DEFAULT_DELAY));
}
/**
@@ -18,26 +55,33 @@ export async function fillAndSubmitCommand(
page: Page,
command: string,
{
- expectStdin = command,
+ expectStdin,
expectStdout,
expectStderr,
+ afterSubmit,
}: {
expectStdin?: string;
expectStdout?: string;
expectStderr?: string;
+ afterSubmit?: () => Promise;
} = {},
) {
+ const expectedStdin = expectStdin ?? command;
const input = await page.getByPlaceholder("Type a command...");
await input.fill(command);
await input.press("Enter");
- const stdin = await getLastStd(page, "stdin");
- await expect(stdin).toHaveText(expectStdin);
+ if (afterSubmit) {
+ await afterSubmit();
+ }
+ const stdin = await getLastStd(page, "stdin", {
+ expectContent: expectedStdin,
+ });
if (expectStdout) {
- const stdout = await getLastStd(page, "stdout");
+ const stdout = await getLastStdAfter(page, "stdout", stdin);
await expect(stdout).toHaveText(expectStdout);
}
if (expectStderr) {
- const stderr = await getLastStd(page, "stderr");
+ const stderr = await getLastStdAfter(page, "stderr", stdin);
await expect(stderr).toHaveText(expectStderr);
}
}
@@ -50,7 +94,7 @@ export async function clickWandButton(
page: Page,
command: string,
{
- expectStdin = command,
+ expectStdin,
expectStdout,
expectStderr,
}: {
@@ -59,13 +103,15 @@ export async function clickWandButton(
expectStderr?: string;
} = {},
) {
+ const expectedStdin = expectStdin ?? command;
await page.getByTitle("Run example command").click({ force: true });
const input = await page.getByPlaceholder("Type a command...");
- await expect(input).toHaveValue(expectStdin);
- const stdin = await getLastStd(page, "stdin");
- await expect(stdin).toHaveText(expectStdin);
+ await expect(input).toHaveValue(expectedStdin);
+ const stdin = await getLastStd(page, "stdin", {
+ expectContent: expectedStdin,
+ });
if (expectStdout) {
- const stdout = await getLastStd(page, "stdout");
+ const stdout = await getLastStdAfter(page, "stdout", stdin);
await expect(stdout).toHaveText(expectStdout);
}
if (expectStderr) {
diff --git a/packages/web-host/tsconfig.app.json b/packages/web-host/tsconfig.app.json
index 3a2dc0f..431fad9 100644
--- a/packages/web-host/tsconfig.app.json
+++ b/packages/web-host/tsconfig.app.json
@@ -25,8 +25,11 @@
"paths": {
"repl:api/host-state": ["./src/wasm/host/host-state.ts"],
"repl:api/http-client": ["./src/wasm/host/http-client.ts"],
- "repl:api/host-state-plugin": ["./src/wasm/host/host-state-plugin.ts"]
+ "repl:api/host-state-plugin": ["./src/wasm/host/host-state-plugin.ts"],
+ "@bytecodealliance/preview2-shim": [
+ "./overrides/@bytecodealliance/preview2-shim"
+ ]
}
},
- "include": ["src", "clis"]
+ "include": ["src", "clis", "overrides"]
}
diff --git a/packages/web-host/vite.config.ts b/packages/web-host/vite.config.ts
index c0730a9..b96618b 100644
--- a/packages/web-host/vite.config.ts
+++ b/packages/web-host/vite.config.ts
@@ -20,6 +20,10 @@ export default defineConfig({
__dirname,
"./src/wasm/host/host-state-plugin.ts",
),
+ "@bytecodealliance/preview2-shim": path.resolve(
+ __dirname,
+ "./overrides/@bytecodealliance/preview2-shim/lib/browser",
+ ),
},
},
base: "/webassembly-component-model-experiments/",