Skip to content

Commit edfa0f9

Browse files
committed
Add JS guest
- Implement clocks/wall-clock - Implement cli/environment Signed-off-by: Doru Blânzeanu <dblnz@pm.me>
1 parent 5dbaba2 commit edfa0f9

File tree

15 files changed

+7276
-4
lines changed

15 files changed

+7276
-4
lines changed

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,23 @@ If you want to follow the manual build instructions, you will also need:
2222

2323
### Building
2424

25+
```sh
26+
# Install JS dependencies
27+
npm install
28+
```
29+
2530
```sh
2631
just build
2732
```
2833

2934
### Running
3035

3136
```sh
32-
just run
37+
# Run Rust
38+
just run-rust
39+
40+
# Run JS
41+
just run-js
3342
```
3443

3544
From another terminal, you can then test the server:
@@ -53,11 +62,16 @@ Compile the WIT and set the environment variables used when building
5362
wasm-tools component wit hyperlight.wit -w -o hyperlight-world.wasm
5463
```
5564

56-
Build:
65+
Build Rust:
5766
```
5867
cargo build
5968
```
6069

70+
Build JS:
71+
```
72+
npm run build
73+
```
74+
6175
### Running
6276

6377
Build the guest component:
@@ -78,6 +92,12 @@ hyperlight-wasm-aot compile --component \
7892

7993
You can then run the server:
8094

95+
Rust:
8196
```sh
8297
cargo run -- out/sample_wasi_http_rust.aot
8398
```
99+
100+
JS:
101+
```sh
102+
cargo run -- out/sample_wasi_http_js.aot
103+
```

eslint.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { defineConfig } from "eslint/config";
2+
import js from "@eslint/js";
3+
import globals from "globals";
4+
5+
export default defineConfig([
6+
{
7+
files: ["**/*.{js,mjs,cjs}"],
8+
plugins: { js },
9+
extends: ["js/recommended"],
10+
},
11+
{
12+
files: ["**/*.{js,mjs,cjs}"],
13+
languageOptions: { globals: globals.browser },
14+
},
15+
]);

guest_js/endpoints/echo.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export { echo, echoHeaders };
2+
3+
// Echo the body of the request in the response
4+
const echo = async (req) => {
5+
const bodyText = req.body ? await req.text() : "No body to echo";
6+
7+
return new Response(bodyText, {
8+
status: 200,
9+
headers: {
10+
"Content-Type": "text/plain",
11+
},
12+
});
13+
};
14+
15+
// Echo all headers from the request in JSON format
16+
const echoHeaders = (req) => {
17+
const headersObj = Object.fromEntries(req.headers.entries());
18+
return new Response(JSON.stringify(headersObj, null, 2), {
19+
status: 200,
20+
headers: {
21+
"Content-Type": "application/json",
22+
},
23+
});
24+
};

guest_js/endpoints/sleep.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export { sleep };
2+
3+
// Async sleep function that returns a response after waiting
4+
const sleep = async (ms) => {
5+
const msNum = parseInt(ms, 10);
6+
await new Promise((resolve) => setTimeout(resolve, msNum));
7+
8+
return new Response(`Sleeping for ${ms}ms...`, {
9+
status: 200,
10+
headers: {
11+
"Content-Type": "text/plain",
12+
},
13+
});
14+
};

guest_js/endpoints/upload.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export { upload };
2+
3+
// Endpoint to handle file uploads using FormData and Blob
4+
const upload = async (req) => {
5+
try {
6+
const blob = await req.blob();
7+
8+
if (!blob || !(blob instanceof Blob)) {
9+
return new Response("File not found in form data.", { status: 400 });
10+
}
11+
12+
const text = await blob.text();
13+
14+
return new Response(text, {
15+
headers: {
16+
"Content-Type": blob.type || "application/octet-stream",
17+
},
18+
});
19+
} catch (error) {
20+
return new Response(`Error processing form data: ${error}`, {
21+
status: 500,
22+
});
23+
}
24+
};

guest_js/server.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { AutoRouter } from "itty-router";
2+
3+
import { sleep } from "./endpoints/sleep.js";
4+
import { upload } from "./endpoints/upload.js";
5+
import { echo, echoHeaders } from "./endpoints/echo.js";
6+
7+
let router = AutoRouter();
8+
9+
router
10+
.get("/", () => new Response("Hello, JS wasi:http world!\n"))
11+
.get("/echo-headers", (req) => echoHeaders(req))
12+
.get("/sleep/:ms", async ({ ms }) => await sleep(ms))
13+
.post("/echo", (req) => echo(req))
14+
.post("/upload", async (req) => await upload(req));
15+
16+
addEventListener("fetch", async (event) => {
17+
event.respondWith(router.fetch(event.request));
18+
});

justfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ BIN_DIR := TARGET_DIR + "/bin"
33
OUT_DIR := justfile_directory() + "/out"
44
default-target := "release"
55

6-
default: run
6+
default: run-rust
77

88
make-out-dir:
99
mkdir -p {{ OUT_DIR }}
@@ -15,6 +15,10 @@ install-hyperlight-wasm-aot:
1515
--version 0.9.0 \
1616
--root {{ TARGET_DIR }}
1717

18+
build-js-component: make-out-dir install-hyperlight-wasm-aot
19+
npm run build
20+
cd {{ OUT_DIR }} && {{ BIN_DIR }}/hyperlight-wasm-aot compile --component sample-wasi-http-js.wasm
21+
1822
build-rust-component target=default-target: make-out-dir install-hyperlight-wasm-aot
1923
cargo component build \
2024
--profile={{ if target == "debug" { "dev" } else { target } }} \
@@ -34,6 +38,8 @@ make-wit-world: install-wasm-tools
3438
build: make-wit-world
3539
cargo build
3640

37-
run: build build-rust-component
41+
run-rust: build build-rust-component
3842
cargo run -- {{ OUT_DIR }}/sample_wasi_http_rust.aot
3943

44+
run-js: build build-js-component
45+
cargo run -- {{ OUT_DIR }}/sample-wasi-http-js.aot

0 commit comments

Comments
 (0)