컴포넌트 모델
WebAssembly Component Model
Overview
The WebAssembly Component Model provides a standardized way to create composable, portable WebAssembly components with high-level interface definitions.
What is the Component Model?
The Component Model is a proposal for WebAssembly that adds:
- Interface definitions (WIT - WebAssembly Interface Types)
- Component composition
- Strong typing across module boundaries
- Language-agnostic interfaces
Vais Support
Vais supports compiling to WebAssembly components that can:
- Export functions with typed interfaces
- Import functions from other components
- Compose with components from other languages
- Run in sandboxed environments
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Vais WebAssembly Component │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Vais Source │─────▶│ LLVM WASM │ │
│ │ (.vais) │ │ Backend │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Core WASM │ │
│ │ Module │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Component │ │
│ │ Adapter │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ WASM │ │
│ │ Component │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
WIT Interface Definitions
Example Interface
calculator.wit:
package vais:calculator
interface calculate {
add: func(a: s32, b: s32) -> s32
subtract: func(a: s32, b: s32) -> s32
multiply: func(a: s32, b: s32) -> s32
divide: func(a: s32, b: s32) -> result<s32, string>
}
world calculator {
export calculate
}
Vais Implementation
calculator.vais:
# Export functions matching WIT interface
F add(a: i32, b: i32) -> i32 {
a + b
}
F subtract(a: i32, b: i32) -> i32 {
a - b
}
F multiply(a: i32, b: i32) -> i32 {
a * b
}
F divide(a: i32, b: i32) -> Result<i32, String> {
I b == 0 {
R Err("Division by zero")
}
Ok(a / b)
}
Type Mappings
WIT to Vais Types
| WIT Type | Vais Type | Description |
|---|---|---|
| s8, s16, s32, s64 | i8, i16, i32, i64 | Signed integers |
| u8, u16, u32, u64 | u8, u16, u32, u64 | Unsigned integers |
| f32, f64 | f32, f64 | Floating point |
| char | char | Unicode character |
| string | String | UTF-8 string |
| bool | bool | Boolean |
| list | Vec | Dynamic array |
| option | Optional | Optional value |
| result<T, E> | Result<T, E> | Result type |
| tuple<A, B> | (A, B) | Tuple |
| record | struct | Record type |
| variant | enum | Variant type |
Records (Structs)
WIT:
record point {
x: f64,
y: f64,
}
Vais:
S Point {
x: f64,
y: f64,
}
Variants (Enums)
WIT:
variant color {
red,
green,
blue,
custom(u8, u8, u8),
}
Vais:
E Color {
Red,
Green,
Blue,
Custom(u8, u8, u8),
}
Building Components
Compile to WASM Component
vaisc build calculator.vais --target wasm32-wasi --component
This generates:
calculator.wasm- Core WebAssembly modulecalculator.component.wasm- Component with adapters
Using Component Tooling
# Inspect component
wasm-tools component wit calculator.component.wasm
# Validate component
wasm-tools validate calculator.component.wasm
# Compose components
wasm-tools compose -o composed.wasm calculator.component.wasm ui.component.wasm
Component Composition
Importing Components
WIT:
package vais:app
interface app {
import vais:calculator/calculate
run: func() -> s32
}
Vais:
# Import calculator component
import calculator::{ add, multiply }
F run() -> i32 {
x := add(10, 20)
y := multiply(x, 2)
y
}
Composing Multiple Components
# Build calculator component
vaisc build calculator.vais --component -o calculator.wasm
# Build app component
vaisc build app.vais --component -o app.wasm
# Compose them
wasm-tools compose \
-d calculator.wasm \
-o composed.wasm \
app.wasm
Runtime Environments
WASI Preview 2
Vais components can run in WASI Preview 2 environments:
# Run with wasmtime
wasmtime run calculator.component.wasm
# Run with wasmer
wasmer run calculator.component.wasm
Browser Environments
Components can run in browsers with polyfills:
import { instantiate } from './calculator.component.js';
const calculator = await instantiate();
const result = calculator.add(5, 3);
console.log('5 + 3 =', result);
Vais Playground
The web playground uses WASM components:
- Client-side compilation
- Sandboxed execution
- Cross-language composition
Advanced Features
Resource Types
WIT:
resource file {
constructor(path: string)
read: func() -> result<string, string>
write: func(data: string) -> result<unit, string>
}
Vais:
S File {
handle: *void,
}
impl File {
F new(path: String) -> File {
# Implementation
}
F read(self: *File) -> Result<String, String> {
# Implementation
}
F write(self: *File, data: String) -> Result<(), String> {
# Implementation
}
}
Async Functions
WIT:
interface async-io {
fetch: func(url: string) -> future<result<string, string>>
}
Vais:
A F fetch(url: String) -> Result<String, String> {
# Async implementation
}
Security and Sandboxing
Capability-Based Security
Components can only access what they import:
world secure-app {
import wasi:filesystem/types
import wasi:sockets/tcp
export run
}
Resource Limits
# Limit memory and CPU
wasmtime run --max-memory 10M --max-wasm-stack 100K component.wasm
Performance
Size Optimization
# Optimize component size
wasm-opt -Os -o optimized.wasm calculator.wasm
# Strip debug info
wasm-strip optimized.wasm
Benchmark Results
Typical performance:
- Instantiation: 1-10ms
- Function calls: Near-native (within 10%)
- Memory overhead: ~1-2MB per component
Debugging
Component Inspector
# View component structure
wasm-tools component wit calculator.component.wasm
# Dump imports/exports
wasm-objdump -x calculator.component.wasm
Debugging in Browser
Use browser DevTools with source maps:
vaisc build app.vais --component --source-map
Best Practices
1. Design Clear Interfaces
Define minimal, focused interfaces:
interface math {
// Good: focused interface
add: func(a: s32, b: s32) -> s32
subtract: func(a: s32, b: s32) -> s32
}
interface utils {
// Avoid: too broad
do-everything: func(input: string) -> string
}
2. Use Appropriate Types
Choose the right type for data:
// Good: specific types
record user {
id: u64,
name: string,
email: string,
}
// Avoid: stringly-typed
record user-bad {
data: string,
}
3. Handle Errors Properly
Use Result types for fallible operations:
F parse_number(s: String) -> Result<i32, String> {
# Return Ok or Err
}
4. Minimize Component Size
- Use only needed imports
- Enable optimization flags
- Strip debug information in production
Tools and Ecosystem
Component Toolchain
wasm-tools- Component manipulationwit-bindgen- Generate bindingswasmtime- Component runtimecargo-component- Rust components
Vais Integration
# Generate WIT from Vais
vaisc wit-export module.vais -o interface.wit
# Generate Vais from WIT
vaisc wit-import interface.wit -o bindings.vais
Examples
See the following examples:
examples/wasm_component_simple.vais- Basic componentexamples/wasm_component_compose.vais- Component compositionexamples/wasm_component_async.vais- Async components
Further Reading
- Implementation:
WASM_COMPONENT_IMPLEMENTATION.md - WASI Docs: https://wasi.dev/
- Component Model Proposal: https://github.com/WebAssembly/component-model
Status
WebAssembly Component Model support is production-ready with full tooling integration.