diff --git a/common.ts b/common.ts index bce697ec..0d37c7e7 100644 --- a/common.ts +++ b/common.ts @@ -1,4 +1,4 @@ -import { VirtualFile } from "./pkg/wasmer_wasix_js"; +import { VirtualFile } from "./pkg/wasmer_wasix_js.js"; export function VirtualFileReader(file: VirtualFile, opts?: { chunkSize?: number, strategy?: QueuingStrategy }): ReadableStream { let chunkSize = opts?.chunkSize || 65536; diff --git a/examples/deno/README.md b/examples/deno/README.md index 2e9c22fc..d66917e5 100644 --- a/examples/deno/README.md +++ b/examples/deno/README.md @@ -5,7 +5,7 @@ This example outputs hello world in the `stdout`. ``` -$ deno run --allow-net helloworld.ts +$ deno run --allow-read helloworld.ts hello world (exit code: 0) ``` @@ -15,7 +15,7 @@ hello world This example lists the files and directories on `/`. ``` -$ deno run --allow-net fs.ts +$ deno run --allow-read fs.ts "./a" "./b" "./file" diff --git a/examples/deno/fs.ts b/examples/deno/fs.ts index df8a3119..997de88e 100644 --- a/examples/deno/fs.ts +++ b/examples/deno/fs.ts @@ -1,45 +1,46 @@ +// import init, { WASI } from "https://deno.land/x/wasm/wasi.ts"; import init, { WASI } from "../../wasix.ts"; -Error.stackTraceLimit += 30; // This is needed to load the WASI library first await init(); -const decoder = new TextDecoder("utf-8"); -const strategy = new ByteLengthQueuingStrategy({ highWaterMark: 4096 }); -const stdout = new WritableStream({ - /** @argument {Uint8Array} chunk */ - write(chunk) { - let text = decoder.decode(chunk, { stream: true }); - console.log(text); - }, - close() { console.log("stdout closed"); }, - abort() { console.log("stdout aborted"); }, -}, strategy); - const wasi = new WASI({ env: {}, args: [], - stdout: stdout.getWriter(), }); -const moduleBytes = fetch( - new URL('../../tests/mapdir.wasm', import.meta.url), -); +// Keep track of open handles (Promises) +let handles: Promise[] = []; + +// pipe wasi.stdout to `stdout` string +let stdout = ""; +const DecodeStream = () => new TextDecoderStream("utf-8", { ignoreBOM: false, fatal: true }); +const StdoutWritable = () => new WritableStream({ + write(chunk, _controller) { stdout += chunk; } +}); +handles.push(wasi.stdout.pipeThrough(DecodeStream()).pipeTo(StdoutWritable())); + +const moduleBytes = fetch(new URL("../../tests/mapdir.wasm", import.meta.url)); const module = await WebAssembly.compileStreaming(moduleBytes); -await wasi.instantiate(module, {}); +wasi.instantiate(module, {}); wasi.fs.createDir("/a"); wasi.fs.createDir("/b"); -{ - const file = wasi.fs.open("/file", { read: true, write: true, create: true }); - await file.writeString("fileContents"); - await file.flush(); - console.log("readString: ", await file.readString()); - file.free(); -} +const file = wasi.fs.open("/file", { read: true, write: true, create: true }); +await file.writeString("fileContents"); +await file.seek(0); const exitCode = wasi.start(); + +// WASI must be freed before handles are closed, either manually, "wasi.free()", or by garbage collection wasi.free(); -// This should print "hello world (exit code: 0)" -console.log(`(exit code: ${exitCode})`); +// Wait for handles to finish before proceeding +Promise.all(handles).then(() => { + // This should print: + // "./a" + // "./b" + // "./file" + // (exit code: 0) + console.log(`${stdout}(exit code: ${exitCode})`); +}); diff --git a/examples/deno/helloworld.ts b/examples/deno/helloworld.ts index 49182b69..23b090ac 100644 --- a/examples/deno/helloworld.ts +++ b/examples/deno/helloworld.ts @@ -1,4 +1,5 @@ -import { init, WASI } from "https://deno.land/x/wasm/wasi.ts"; +// import init, { WASI } from "https://deno.land/x/wasm/wasi.ts"; +import init, { WASI } from "../../wasix.ts"; // This is needed to load the WASI library first await init(); @@ -8,13 +9,30 @@ const wasi = new WASI({ args: [], }); -const moduleBytes = fetch( - "https://cdn.deno.land/wasm/versions/v1.0.2/raw/tests/demo.wasm", -); +// Keep track of open handles (Promises) +let handles: Promise[] = []; + +// pipe wasi.stdout to `stdout` string +let stdout = ""; +const DecodeStream = () => new TextDecoderStream("utf-8", { ignoreBOM: false, fatal: true }); +const StdoutWritable = () => new WritableStream({ + write(chunk, _controller) { stdout += chunk; } +}); +handles.push(wasi.stdout.pipeThrough(DecodeStream()).pipeTo(StdoutWritable())); + + +const moduleBytes = fetch(new URL("../../tests/demo.wasm", import.meta.url)); const module = await WebAssembly.compileStreaming(moduleBytes); await wasi.instantiate(module, {}); const exitCode = wasi.start(); -const stdout = wasi.getStdoutString(); -// This should print "hello world (exit code: 0)" -console.log(`${stdout}(exit code: ${exitCode})`); + +// WASI must be freed before handles are closed, either manually, "wasi.free()", or by garbage collection +wasi.free(); +// Wait for handles to finish before proceeding +Promise.all(handles).then(() => { + // This should print: + // hello world + // (exit code: 0) + console.log(`${stdout}(exit code: ${exitCode})`); +}); diff --git a/examples/node/README.md b/examples/node/README.md index f4778f05..74e6b0b4 100644 --- a/examples/node/README.md +++ b/examples/node/README.md @@ -1,11 +1,5 @@ # Wasmer/WASI Node examples -You can run the code with: - -```shell -npm i -``` - ## Hello World This example outputs hello world in the `stdout`. @@ -26,4 +20,4 @@ $ node fs.mjs "./b" "./file" (exit code: 0) -``` \ No newline at end of file +``` diff --git a/examples/node/fs.mjs b/examples/node/fs.mjs index 5e73bf00..e3c45eae 100644 --- a/examples/node/fs.mjs +++ b/examples/node/fs.mjs @@ -1,75 +1,50 @@ -// This should print -// ``` -// hello world -// (exit code: 0) -// ``` - -import fs from "node:fs"; -import url from "node:url"; -import stream from "node:stream"; -import init, { WASI, TtyState, VirtualFileReader, VirtualFileWriter } from "../../dist/lib.mjs"; -Error.stackTraceLimit += 30; +import fs from "fs"; +// import init, { WASI } from "@wasmer/wasi"; +import init, { WASI } from "../../dist/lib.min.mjs"; -// This is needed to load the WASI library first, must be called before API can be used +// This is needed to load the WASI library first await init(); -const defaultTtyState = Object.assign(new TtyState(), { - columns: 800, - rows: 250, -}); - let wasi = new WASI({ env: {}, args: [], - // define the starting state of the tty - tty: defaultTtyState, }); -// keep track of tty/io Promises +// Keep track of open handles (Promises) let handles = []; -let stdoutBuf = ""; -{ // setup tty and stdio handlers - handles.push( - (() => { - const handle = wasi.tty; - const handler = new TransformStream({ transform(chunk, controller) { controller.enqueue(chunk ? chunk : defaultTtyState) } }); - return handle.readable.pipeThrough(handler).pipeTo(handle.writable); - })() - ); - handles.push( - (() => { - const decoder = new TransformStream({ - start(controller) { this.decoder = new TextDecoder("utf-8"); }, - transform(chunk, controller) { controller.enqueue(this.decoder.decode(chunk, { stream: true })) }, - }); - const handler = new WritableStream({ - write(chunk, _controller) { stdoutBuf += chunk; } - }); - return wasi.stdout.pipeThrough(decoder).pipeTo(handler); - })() - ); -} +// pipe wasi.stdout to `stdout` string +let stdout = ""; +const DecodeStream = () => new TextDecoderStream("utf-8", { ignoreBOM: false, fatal: true }); +const StdoutWritable = () => new WritableStream({ + write(chunk, _controller) { stdout += chunk; } +}); +handles.push(wasi.stdout.pipeThrough(DecodeStream()).pipeTo(StdoutWritable())); -{ // test fs - wasi.fs.createDir("/a"); - wasi.fs.createDir("/b"); - let file = wasi.fs.open("/file", { read: true, write: true, create: true }); - console.log("await file.writeString(\"fileContents\") ... ", await file.writeString("fileContents")); - await file.flush(); - console.log("await file.text() ... ", await file.text()); - file.free(); -} +const buf = fs.readFileSync(new URL('../../tests/mapdir.wasm', import.meta.url)); -const wasm = fs.readFileSync(url.fileURLToPath(new URL('../../tests/mapdir.wasm', import.meta.url))); const module = await WebAssembly.compile( - new Uint8Array(wasm) + new Uint8Array(buf) ); wasi.instantiate(module, {}); + +wasi.fs.createDir("/a"); +wasi.fs.createDir("/b"); + +let file = wasi.fs.open("/file", { read: true, write: true, create: true }); +await file.writeString("fileContents"); +await file.seek(0); + let exitCode = wasi.start(); -// You must call WASI.free() before handles can resolve +// WASI must be freed before handles are closed, either manually, "wasi.free()", or by garbage collection wasi.free(); -// Await handles to flush tty/io +// Wait for handles to finish before proceeding await Promise.all(handles); -console.log(`${stdoutBuf}(exit code: ${exitCode})`); + +// This should print: +// "./a" +// "./b" +// "./file" +// (exit code: 0) +console.log(`${stdout}(exit code: ${exitCode})`); diff --git a/examples/node/helloworld.mjs b/examples/node/helloworld.mjs index 66056a99..0b963b8c 100644 --- a/examples/node/helloworld.mjs +++ b/examples/node/helloworld.mjs @@ -1,5 +1,6 @@ import fs from "fs"; -import { init, WASI } from "@wasmer/wasi"; +// import init, { WASI } from "@wasmer/wasi"; +import init, { WASI } from "../../dist/lib.min.mjs"; // This is needed to load the WASI library first await init(); @@ -9,14 +10,32 @@ let wasi = new WASI({ args: [], }); -const buf = fs.readFileSync('../../tests/demo.wasm'); +// Keep track of open handles (Promises) +let handles = []; + +// pipe wasi.stdout to `stdout` string +let stdout = ""; +const DecodeStream = () => new TextDecoderStream("utf-8", { ignoreBOM: false, fatal: true }); +const StdoutWritable = () => new WritableStream({ + write(chunk, _controller) { stdout += chunk; } +}); +handles.push(wasi.stdout.pipeThrough(DecodeStream()).pipeTo(StdoutWritable())); + +const buf = fs.readFileSync(new URL('../../tests/demo.wasm', import.meta.url)); const module = await WebAssembly.compile( new Uint8Array(buf) ); -await wasi.instantiate(module, {}); +wasi.instantiate(module, {}); let exitCode = wasi.start(); -let stdout = wasi.getStdoutString(); -// This should print "hello world (exit code: 0)" + +// WASI must be freed before handles are closed, either manually, "wasi.free()", or by garbage collection +wasi.free(); +// Wait for handles to finish before proceeding +await Promise.all(handles); + +// This should print: +// hello world +// (exit code: 0) console.log(`${stdout}(exit code: ${exitCode})`); diff --git a/pkg/wasmer_wasix_js_bg.wasm b/pkg/wasmer_wasix_js_bg.wasm index 0e50d7b0..9901a6c8 100644 Binary files a/pkg/wasmer_wasix_js_bg.wasm and b/pkg/wasmer_wasix_js_bg.wasm differ diff --git a/wasix.ts b/wasix.ts index d0279491..21097d4a 100644 --- a/wasix.ts +++ b/wasix.ts @@ -1,8 +1,8 @@ // @deno-types="./pkg/wasmer_wasix_js.d.ts" import _init from "./pkg/wasmer_wasix_js.js"; // @deno-types="./pkg/wasmer_wasix_js.d.ts" -export * from "./pkg/wasmer_wasix_js"; -export * from "./common"; +export * from "./pkg/wasmer_wasix_js.js"; +export * from "./common.ts"; let inited: Promise | null = null; export default async function init(...args: any[]) {