Zum Inhalt springen

WebAssembly (mit WASI) ​

WebAssembly ist eine sichere, isolierte und portable Laufzeitumgebung, die innerhalb und außerhalb von Webbrowsern ausgeführt wird.

In der Praxis:

  • Sprachen wie JavaScript werden zu WebAssembly kompiliert (.wasm-Dateien).
  • WebAssembly-Laufzeitumgebungen wie wasmtime oder jco ermöglichen das Ausführen von WebAssembly-Binärdateien.

Während WebAssembly selbst keinen Zugriff auf das lokale Dateisystem, Sockets oder Ähnliches hat, ermöglicht das WebAssembly System Interface, eine Plattform für WebAssembly-Workloads zu definieren.

Das bedeutet, dass WebAssembly mit WASI mit Dateien, Sockets und vielem mehr arbeiten kann.

Info

Möchtest du dir die WASI-Schnittstelle selbst ansehen? Schau dir wasi:http an.

Die Unterstützung für WebAssembly mit WASI in JavaScript basiert auf StarlingMonkey. Da sowohl StarlingMonkey als auch Hono sich auf Webstandards konzentrieren, funktioniert Hono *ohne zusätzliche Anpassungen in WASI-fähigen WebAssembly-Ökosystemen.

1. Einrichtung ​

Das JavaScript-Ökosystem von WebAssembly bietet Werkzeuge, mit denen du einfach WASI-fähige WebAssembly-Komponenten erstellen kannst:

  • StarlingMonkey ist ein Fork von SpiderMonkey, der zu WebAssembly kompiliert wird und Komponenten ermöglicht.
  • componentize-js wandelt JavaScript-ES-Module in WebAssembly-Komponenten um.
  • jco ist ein Mehrzweckwerkzeug, das Komponenten baut, Typen generiert und Komponenten in Umgebungen wie Node.js oder dem Browser ausführt.

Info

WebAssembly hat ein offenes Ökosystem und ist quelloffen. Die Kernprojekte werden hauptsächlich von der Bytecode Alliance und ihren Mitgliedern betreut.

Neue Funktionen, Fehlermeldungen, Pull Requests und andere Beiträge sind jederzeit willkommen.

Eine Hono-Projektvorlage für WebAssembly ist noch nicht verfügbar. Du kannst ein WebAssembly-Hono-Projekt aber wie jedes andere Projekt beginnen:

sh
mkdir my-app
cd my-app
npm init
npm i hono
npm i -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
npm i -D rolldown
sh
mkdir my-app
cd my-app
npm init
yarn add hono
yarn add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
yarn add -D rolldown
sh
mkdir my-app
cd my-app
pnpm init --init-type module
pnpm add hono
pnpm add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std
pnpm add -D rolldown
sh
mkdir my-app
cd my-app
npm init
bun add hono
bun add -D @bytecodealliance/jco @bytecodealliance/componentize-js @bytecodealliance/jco-std

Info

Damit dein Projekt ES-Module verwendet, stelle sicher, dass type in package.json auf "module" gesetzt ist.

Nachdem du in den Ordner my-app gewechselt bist, installiere die Abhängigkeiten und initialisiere TypeScript:

sh
npm i
npx tsc --init
sh
yarn
yarn tsc --init
sh
pnpm i
pnpm exec tsc --init
sh
bun i

Wenn du eine grundlegende TypeScript-Konfigurationsdatei (tsconfig.json) erstellt hast, stelle sicher, dass sie die folgende Einstellung enthält:

  • Setze compilerOptions.module auf "nodenext".

Da componentize-js und jco, das es ebenfalls verwendet, nur einzelne JavaScript-Dateien unterstützen, ist das Bündeln erforderlich. Mit rolldown kannst du ein Bundle aus einer einzelnen Datei erstellen.

Du kannst eine Rolldown-Konfiguration (rolldown.config.mjs) wie diese verwenden:

js
import { defineConfig } from 'rolldown'

export default defineConfig({
  input: 'src/component.ts',
  external: /wasi:.*/,
  output: {
    file: 'dist/component.js',
    format: 'esm',
  },
})

Info

Du kannst auch jeden anderen Bundler verwenden, mit dem du besser vertraut bist (rolldown, esbuild, rollup usw.).

2. WIT-Schnittstelle und Abhängigkeiten einrichten ​

WebAssembly Interface Types (WIT) ist eine Schnittstellenbeschreibungssprache („IDL“), die festlegt, welche Funktionen eine WebAssembly-Komponente verwendet („Imports“) und welche sie bereitstellt („Exports“).

Unter den standardisierten WIT-Schnittstellen dient wasi:http der Verarbeitung von HTTP-Anfragen, sowohl beim Empfangen als auch beim Senden. Da wir einen Webserver erstellen wollen, muss unsere Komponente die Verwendung von wasi:http/incoming-handler in ihrer WIT-World deklarieren:

Richte zunächst die WIT-World der Komponente in einer Datei namens wit/component.wit ein:

txt
package example:hono;

world component {
    export wasi:http/incoming-handler@0.2.6;
}

Einfach ausgedrückt bedeutet die obige WIT-Datei, dass unsere Komponente die Funktionalität zum Empfangen und Verarbeiten eingehender HTTP-Anfragen „bereitstellt“.

Die Schnittstelle wasi:http/incoming-handler hängt von vorgelagerten standardisierten WIT-Schnittstellen ab, etwa Spezifikationen zur Struktur von Anfragen.

Um diese von der Bytecode Alliance gepflegten WIT-Schnittstellen von Drittanbietern herunterzuladen, können wir das Werkzeug wkg verwenden:

sh
wkg wit fetch

Sobald wkg fertig ist, sollte dein Ordner wit neben component.wit auch einen neuen Ordner deps enthalten:

wit
├── component.wit
└── deps
    ├── wasi-cli-0.2.6
    │   └── package.wit
    ├── wasi-clocks-0.2.6
    │   └── package.wit
    ├── wasi-http-0.2.6
    │   └── package.wit
    ├── wasi-io-0.2.6
    │   └── package.wit
    └── wasi-random-0.2.6
        └── package.wit

3. Hello Wasm ​

Um einen HTTP-Server in WebAssembly zu erstellen, können wir das Projekt [jco-std][jco-std] verwenden. Es enthält Helfer, durch die sich die Entwicklung sehr ähnlich wie mit gewöhnlichem Hono anfühlt.

Implementieren wir unsere World component mit einer einfachen Hono-Anwendung als WebAssembly-Komponente in einer Datei namens src/component.ts:

ts
import { Hono } from 'hono'
import { fire } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'

const app = new Hono()

app.get('/hello', (c) => {
  return c.json({ message: 'Hello from WebAssembly!' })
})

fire(app)

// Although we've called `fire()` with wasi HTTP configured for use above,
// we still need to actually export the `wasi:http/incoming-handler` interface object,
// as jco and componentize-js will be looking for the ES module export that matches the WASI interface.
export { incomingHandler } from '@bytecodealliance/jco-std/wasi/0.2.6/http/adapters/hono/server'

4. Build ​

Da wir Rolldown verwenden und es für die TypeScript-Kompilierung eingerichtet haben, können wir damit bauen und bündeln:

sh
npx rolldown -c
sh
yarn rolldown -c
sh
pnpm exec rolldown -c
sh
bun build --target=bun --outfile=dist/component.js ./src/component.ts

Info

Das Bündeln ist erforderlich, da die Werkzeuge des JavaScript-Ökosystems von WebAssembly momentan nur eine einzelne JavaScript-Datei unterstützen und wir Hono sowie verwandte Bibliotheken einschließen möchten.

Für Komponenten mit einfacheren Anforderungen sind keine Bundler erforderlich.

Um deine WebAssembly-Komponente zu bauen, verwende jco und damit indirekt componentize-js:

sh
npx jco componentize -w wit -o dist/component.wasm dist/component.js
sh
yarn jco componentize -w wit -o dist/component.wasm dist/component.js
sh
pnpm exec jco componentize -w wit -o dist/component.wasm dist/component.js
sh
bun run jco componentize -w wit -o dist/component.wasm dist/component.js

5. Ausführen ​

Um deinen Hono-WebAssembly-HTTP-Server auszuführen, kannst du jede WASI-fähige WebAssembly-Laufzeitumgebung verwenden:

In dieser Anleitung verwenden wir jco serve, da es bereits installiert ist.

Achtung

jco serve ist für die Entwicklung gedacht und wird nicht für den Einsatz in der Produktion empfohlen.

sh
npx jco serve dist/component.wasm
sh
yarn jco serve dist/component.wasm
sh
pnpm exec jco serve dist/component.wasm
sh
bun run jco serve dist/component.wasm

Du solltest eine Ausgabe wie diese sehen:

$ npx jco serve dist/component.wasm
Server listening @ localhost:8000...

Eine Anfrage an localhost:8000/hello erzeugt die JSON-Ausgabe, die du in deiner Hono-Anwendung festgelegt hast.

Du solltest eine Ausgabe wie diese sehen:

json
{ "message": "Hello from WebAssembly!" }

Info

jco serve konvertiert die WebAssembly-Komponente in ein grundlegendes WebAssembly-Kernmodul, damit sie in Laufzeitumgebungen wie Node.js und dem Browser ausgeführt werden kann.

Dieser Vorgang wird normalerweise über jco transpile ausgeführt. Dadurch können wir JavaScript-Umgebungen wie Node.js und den Browser, die V8 oder andere JavaScript-Engines verwenden können, als Laufzeitumgebungen für WebAssembly-Komponenten nutzen.

Die Funktionsweise von jco transpile liegt außerhalb des Umfangs dieser Anleitung. Weitere Informationen findest du im Jco-Handbuch.

Weitere Informationen ​

Mehr über WASI, WebAssembly-Komponenten und andere Themen erfährst du in den folgenden Ressourcen:

Für Fragen, Kommentare, Beiträge oder Fehlermeldungen an die WebAssembly-Community:

Veröffentlicht unter der MIT-Lizenz.