Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.original
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# see https://github.com/WebAssembly/wasi-sdk/releases for exact possibilities
WASI_OS=macos # 'macos' | 'linux' | ''windows'
WASI_ARCH=x86_64 # 'x86_64' | 'arm64'
WASI_VERSION=25
WASI_VERSION_FULL=${WASI_VERSION}.0
22 changes: 22 additions & 0 deletions .github/workflows/rust-host.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Set variables based on OS and architecture for just dl-wasi-sdk
run: |
if [ "${{ runner.arch }}" = "X64" ]; then
echo "WASI_ARCH=x86_64" >> $GITHUB_ENV
else
echo "WASI_ARCH=arm64" >> $GITHUB_ENV
fi
if [ "${{ runner.os }}" = "Windows" ]; then
echo "WASI_OS=windows" >> $GITHUB_ENV
else
echo "WASI_OS=linux" >> $GITHUB_ENV
fi
echo "WASI_VERSION_FULL=25.0" >> $GITHUB_ENV
echo "WASI_VERSION=25" >> $GITHUB_ENV
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
Expand All @@ -15,6 +29,14 @@ jobs:
- uses: extractions/setup-just@v3
- name: Install cargo-component
run: cargo binstall cargo-component@0.21.1
- name: Install wasm-tools
run: cargo binstall wasm-tools@1.235.0
- name: Install wit-bindgen
run: cargo install wit-bindgen-cli@0.43.0
- name: Install wasi-sdk
run: |
mkdir c_deps
just dl-wasi-sdk
- name: Build
run: just build
- name: Test
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/web-host.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set variables based on OS and architecture for just dl-wasi-sdk
run: |
if [ "${{ runner.arch }}" = "X64" ]; then
echo "WASI_ARCH=x86_64" >> $GITHUB_ENV
else
echo "WASI_ARCH=arm64" >> $GITHUB_ENV
fi
if [ "${{ runner.os }}" = "Windows" ]; then
echo "WASI_OS=windows" >> $GITHUB_ENV
else
echo "WASI_OS=linux" >> $GITHUB_ENV
fi
echo "WASI_VERSION_FULL=25.0" >> $GITHUB_ENV
echo "WASI_VERSION=25" >> $GITHUB_ENV
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
Expand All @@ -18,6 +32,14 @@ jobs:
- uses: extractions/setup-just@v3
- name: Install cargo-component
run: cargo binstall cargo-component@0.21.1
- name: Install wasm-tools
run: cargo binstall wasm-tools@1.235.0
- name: Install wit-bindgen
run: cargo install wit-bindgen-cli@0.43.0
- name: Install wasi-sdk
run: |
mkdir c_deps
just dl-wasi-sdk
- name: Install JavaScript dependencies
run: npm ci
- name: Build
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ target/
### JavaScript ###
node_modules/
dist/

# C
.env
c_deps/
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"skellock.just",
"biomejs.biome",
"bradlc.vscode-tailwindcss",
"ms-playwright.playwright"
"ms-playwright.playwright",
"ms-vscode.cpptools"
]
}
63 changes: 54 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ There are two kinds of hosts:
Those hosts then run the same codebase which is compiled to WebAssembly:

- the REPL logic
- the plugins
- the plugins (made a few in rust, C and TypeScript)

Security model: the REPL cli implements a security model inspired by [deno](https://docs.deno.com/runtime/fundamentals/security/#permissions):

Expand Down Expand Up @@ -63,7 +63,7 @@ In the last seven years I've done a few projects involving rust and WebAssembly:

## Usage

### pluginlab (rust)
### pluginlab (rust) - REPL cli host

#### Install

Expand All @@ -82,6 +82,7 @@ pluginlab\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_echo.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_weather.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_cat.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin-echo-c.wasm\
--allow-all
```

Expand All @@ -105,6 +106,7 @@ pluginlab\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_echo.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_weather.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_cat.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin-echo-c.wasm\
--allow-all
[Host] Starting REPL host...
[Host] Loading REPL logic from: https://topheman.github.io/webassembly-component-model-experiments/plugins/repl_logic_guest.wasm
Expand Down Expand Up @@ -169,7 +171,31 @@ npm install
npx playwright install
```

### pluginlab (rust)
#### C tooling

[From the WebAssembly Component Model section for C tooling](https://component-model.bytecodealliance.org/language-support/c.html)

```bash
# Initialize the .env file tracking the WASI SDK version for C development
# You will be asked to update the WASI_OS and WASI_ARCH variables if needed
just init-env-file
```

```bash
cargo install wit-bindgen-cli@0.43.0
```

```bash
# Install the wasm-tools tool - you can also use cargo install wasm-tools@1.235.0 if you don't have cargo-binstall
cargo binstall wasm-tools@1.235.0
```

```bash
# Download the WASI SDK into ./c_deps/wasi-sdk folder
just dl-wasi-sdk
```

### pluginlab (rust) - REPL cli host

#### Build

Expand All @@ -182,6 +208,7 @@ This will (see [justfile](./justfile)):
- compile the pluginlab crate from rust to a binary file
- compile the repl-logic-guest crate from rust to wasm
- compile the plugin-* crates from rust to wasm
- compile the c_modules/plugin-* C plugins to wasm

#### Run

Expand All @@ -193,6 +220,7 @@ This will (see [justfile](./justfile)):
--plugins ./target/wasm32-wasip1/debug/plugin_echo.wasm\
--plugins ./target/wasm32-wasip1/debug/plugin_weather.wasm\
--plugins ./target/wasm32-wasip1/debug/plugin_cat.wasm\
--plugins ./c_modules/plugin-echo/plugin-echo-c.wasm\
--allow-all
```

Expand Down Expand Up @@ -300,14 +328,23 @@ In [`.github/workflows/web-host.yml`](./.github/workflows/web-host.yml), after t

To be sure that the preview server is up and running before running the tests, we use the [`webServer.command` option](https://playwright.dev/docs/test-webserver) of [playwright.config.ts](./packages/web-host/playwright.config.ts) to run `WAIT_FOR_SERVER_AT_URL=http://localhost:4173/webassembly-component-model-experiments/ npm run test:e2e:all:preview`

### plugins

There are currently plugins implemented in 3 languages (most of them are in rust):

#### Rust

### plugins (TypeScript)
You can write plugins in rust in [`crates/plugin-*`](./crates).

You can write plugins in rust in [`crates/plugin-*`](./crates), you can also write plugins in TypeScript in [`packages/plugin-*`](./packages), thanks to `jco componentize` (based on [componentize-js](https://github.com/bytecodealliance/componentize-js)).
#### C

There is a [`packages/plugin-echo`](./packages/plugin-echo/) example plugin in TypeScript.
You can write plugins in C in [`c_modules/plugin-*`](./c_modules), thanks to `wit-bindgen` (based on [wit-bindgen](https://github.com/bytecodealliance/wit-bindgen)).

The downsides of writing plugins in TypeScript is mostly that your `.wasm` file will be **much larger** than the one compiled from rust:
#### TypeScript

You can also write plugins in TypeScript in [`packages/plugin-*`](./packages), thanks to `jco componentize` (based on [componentize-js](https://github.com/bytecodealliance/componentize-js)).

The downsides of writing plugins in TypeScript is mostly that your `.wasm` file will be **much larger** than the one compiled from rust or C:

- ~100KB of wasm for the rust plugin
- 11MB of wasm for the TypeScript plugin
Expand All @@ -316,9 +353,10 @@ The reason is that a JavaScript runtime needs to be embedded in the `.wasm` file

More about the [SpiderMonkey runtime embedding](https://github.com/bytecodealliance/ComponentizeJS?tab=readme-ov-file#explainer).

### plugins (Other languages)
#### Other languages

Coming.

Coming soon.

## Developer experience

Expand Down Expand Up @@ -346,6 +384,7 @@ Those are **optional** tools that are handy for WebAssembly development:
- [cargo component 0.21.1+](https://github.com/bytecodealliance/cargo-component?tab=readme-ov-file#installation)
- [wasm-tools 1.235.0](https://github.com/bytecodealliance/wasm-tools?tab=readme-ov-file#installation)
- [wasm-opt 116](https://github.com/WebAssembly/binaryen?tab=readme-ov-file#installation)
- [wit-bindgen-cli 0.43.0](https://github.com/bytecodealliance/wit-bindgen)

```bash
# latest versions
Expand All @@ -356,3 +395,9 @@ cargo binstall cargo-component wasm-tools wasm-opt
# specific versions I used for this project
cargo binstall cargo-component@0.21.1 wasm-tools@1.235.0 wasm-opt@116
```

### C tooling

- [From the WebAssembly Component Model section for C tooling](https://component-model.bytecodealliance.org/language-support/c.html)
- [WASI SDK](https://github.com/WebAssembly/wasi-sdk)
- [WIT Bindgen](https://github.com/bytecodealliance/wit-bindgen)
9 changes: 9 additions & 0 deletions c_modules/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Plugins written in C

You can find here plugins written in C, some are re-implementations of the plugins written in Rust.

The `plugin_api.c`, `plugin_api.h` and `plugin_api.component_type.o` are generated with [`wit-bindgen`](https://github.com/bytecodealliance/wit-bindgen), based on the [wit files](../crates/pluginlab/wit) of the project.

The wasm files are compiled with the [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) you downloaded with `just dl-wasi-sdk`, which contain the `clang` compiler.

All you have to do is run `just build` to build everything (including the C plugins) and `just test` to run the tests (including the C plugins).
4 changes: 4 additions & 0 deletions c_modules/plugin-echo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.wasm
plugin_api.h
plugin_api.c
plugin_api_component_type.o
85 changes: 85 additions & 0 deletions c_modules/plugin-echo/component.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "plugin_api.h"
#include <string.h>
#include <stdlib.h>

/*
* C implementation of the echo plugin
*
* IMPLEMENTATION SOURCE:
* This implements the same interface as the Rust version in crates/plugin-echo/src/lib.rs
* The function signatures are generated from the WIT interface by wit-bindgen:
* - exports_repl_api_plugin_name() corresponds to fn name() -> String
* - exports_repl_api_plugin_man() corresponds to fn man() -> String
* - exports_repl_api_plugin_run() corresponds to fn run(payload: String) -> Result<PluginResponse, ()>
*
* MEMORY MANAGEMENT:
* - Input parameters (like payload) are owned by the runtime - DO NOT free them
* - Output parameters (like ret) are populated by us, freed by the runtime
* - plugin_api_string_dup() allocates new memory for string copies
* - The generated _free functions handle cleanup automatically
* - No explicit free() calls needed in plugin code
*/

void exports_repl_api_plugin_name(plugin_api_string_t *ret)
{
// Populate ret with "echo" as the plugin name
// plugin_api_string_dup() allocates new memory and copies the string
plugin_api_string_dup(ret, "echoc");
}

void exports_repl_api_plugin_man(plugin_api_string_t *ret)
{
// Populate ret with the manual text for the echo command
// plugin_api_string_dup() allocates new memory and copies the string
const char *man_text =
"\n"
"NAME\n"
" echoc - Echo a message (built with C)\n"
"\n"
"USAGE\n"
" echoc <message>\n"
"\n"
"DESCRIPTION\n"
" Echo a message.\n"
"\n"
" ";
plugin_api_string_dup(ret, man_text);
}

bool exports_repl_api_plugin_run(plugin_api_string_t *payload, exports_repl_api_plugin_plugin_response_t *ret)
{
// Set status to success (0 = success, 1 = error)
ret->status = REPL_API_TRANSPORT_REPL_STATUS_SUCCESS;

// Set stdout to contain the payload
// is_some = true means the optional string has a value
ret->stdout.is_some = true;

// Create a properly null-terminated string from the payload
// The payload has ptr and len, we need to ensure it's null-terminated
char *temp_str = malloc(payload->len + 1);
if (temp_str == NULL)
{
// Handle allocation failure
ret->stdout.is_some = false;
ret->stderr.is_some = false;
return false;
}

// Copy the payload data and null-terminate it
memcpy(temp_str, payload->ptr, payload->len);
temp_str[payload->len] = '\0';

// Use plugin_api_string_dup to create the output string
plugin_api_string_dup(&ret->stdout.val, temp_str);

// Free our temporary string
free(temp_str);

// Set stderr to none (no error output)
ret->stderr.is_some = false;

// Return true for success (false would indicate an error)
// This corresponds to Ok(response) in the Rust Result<T, ()> pattern
return true;
}
4 changes: 3 additions & 1 deletion crates/pluginlab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ There are two kinds of hosts:
Those hosts then run the same codebase which is compiled to WebAssembly:

- the REPL logic
- the plugins
- the plugins (made a few in rust, C and TypeScript)

Security model: the REPL cli implements a security model inspired by [deno](https://docs.deno.com/runtime/fundamentals/security/#permissions):

Expand Down Expand Up @@ -61,6 +61,7 @@ pluginlab\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_echo.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_weather.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_cat.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin-echo-c.wasm\
--allow-all
```

Expand All @@ -85,6 +86,7 @@ pluginlab\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_echo.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_weather.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin_cat.wasm\
--plugins https://topheman.github.io/webassembly-component-model-experiments/plugins/plugin-echo-c.wasm\
--allow-all
[Host] Starting REPL host...
[Host] Loading REPL logic from: https://topheman.github.io/webassembly-component-model-experiments/plugins/repl_logic_guest.wasm
Expand Down
Loading
Loading