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
463 changes: 139 additions & 324 deletions README.md

Large diffs are not rendered by default.

116 changes: 34 additions & 82 deletions component.go
Original file line number Diff line number Diff line change
@@ -1,124 +1,76 @@
// Package component runs WebAssembly Component Model components -- and the WASI
// 0.2 (wasip2) world built on it -- through Wago's component plugin.
// Package component runs WebAssembly Component Model binaries through Wago.
//
// Where the core Wago package instantiates core modules, this package
// instantiates *components*: genuine wasm32-wasip2 binaries produced by rustc,
// wasm-tools, and friends. It decodes the component, wires its multi-module
// graph (nested instances, canonical lift/lower of the Canonical ABI, resource
// lifetimes). A host module such as wago-org/wasi provides the WASI 0.2
// interfaces (wasi:cli, clocks, filesystem, io, random, sockets, http).
// The package owns Component Model decoding, graph linking, Canonical ABI
// lift/lower, resources, and typed host imports. Core Wasm compilation and
// execution stay behind three reviewed Wago authorities. WASI and other world
// policy belong in plugins that consume Contract.
//
// Typical use: build a Runtime, instantiate a component with the WASI surface
// wired to your stdio/filesystem/args, call an export, then Close.
// A consuming plugin declares Contract in its PluginDefinition and acquires a
// typed reference during registration:
//
// r := wago.NewRuntime()
// defer r.Close()
// components, err := component.Enable(r)
// if err != nil {
// return err
// }
// components, err := plugin.Require(reg, component.Contract)
//
// inst, err := components.Instantiate(ctx, componentWasm,
// wasip2.With(wasip2.Config{Stdout: os.Stdout})...)
// if err != nil {
// return err
// }
// defer inst.Close(ctx)
// Calls stay inside both the contract lease and the component instance's
// lifetime:
//
// // A wasi:cli/command component: run its entry point.
// _, err = inst.Call(ctx, "wasi:cli/run@0.2.3#run")
// err := components.With(func(service component.Service) error {
// return service.WithInstance(ctx, componentWasm, func(in *component.Instance) error {
// _, err := in.Call(ctx, "wasi:cli/run@0.2.3#run")
// return err
// })
// })
//
// Call arguments and results are Go values (uint32, int64, string, []any for
// lists/records, and uint32 handles for resources), matching the Canonical
// ABI's lifting of the component's WIT types.
//
// This API is young and, like the rest of Wago, makes no stability promise yet.
// The service closes the instance before WithInstance returns. Neither the
// service nor the instance may be retained outside its callback.
package component

import (
"context"

"github.com/wago-org/component-model/internal/abi"
"github.com/wago-org/component-model/internal/instance"
"github.com/wago-org/wago"
)

// Instance is a live component instance. Call its exports with Call /
// CallExport, and release it with Close. A wasi:http/incoming-handler component
// also satisfies http.Handler via ServeHTTP.
// Instance is a live component instance. It is valid only during the
// Service.WithInstance callback that supplied it.
type Instance = instance.Instance

// PendingCall is a live CallAsync invocation, suspended awaiting external
// import completions. See Instance.CallAsync.
type PendingCall = instance.PendingCall

// Option configures Instantiate. WithWASI and WithCompileCache produce Options.
// Option configures one component instantiation.
type Option = instance.Option

// CompileCache amortizes a component's decode and its embedded core modules'
// compilation across repeated Instantiate calls of the same component bytes.
// Safe for concurrent use. Pair one with a single Runtime and Close it when
// done. See WithCompileCache and NewCompileCache.
// CompileCache amortizes component decoding and embedded core-module
// compilation across repeated Service.WithInstance calls. A cache belongs to
// one loaded Component Model provider and must be closed before that runtime.
type CompileCache = instance.CompileCache

// Instantiate resolves the Component Model plugin installed on r and delegates
// to its runtime-scoped service. Call Enable once before using this compatibility
// entry point. New code can retain the *Runtime returned by Enable and call its
// Instantiate method directly.
func Instantiate(ctx context.Context, r *wago.Runtime, componentBytes []byte, opts ...Option) (*Instance, error) {
components, err := FromRuntime(r)
if err != nil {
return nil, err
}
return components.Instantiate(ctx, componentBytes, opts...)
}

// WithCompileCache reuses cache across this and future Instantiate calls of the
// same component bytes, skipping the repeated decode + core-module compile.
// WithCompileCache reuses cache across component instantiations.
func WithCompileCache(cache *CompileCache) Option { return instance.WithCompileCache(cache) }

// NewCompileCache returns an empty CompileCache ready to pass to
// WithCompileCache. Close it (CompileCache.Close) alongside the Runtime it is
// paired with.
// NewCompileCache returns an empty compile cache. Close it before closing the
// Wago runtime that loaded the Component Model provider.
func NewCompileCache() *CompileCache { return instance.NewCompileCache() }

// Value is a component-level call value: a Go value matching the Canonical
// ABI's lifting of a WIT type (uint32, int64, float64, string, []any for
// lists/records/tuples, uint32 for resource handles). It is the element type of
// Call/CallExport arguments and results and of host-import args/results.
// Value is a component-level call value matching the Canonical ABI lifting of
// a WIT type.
type Value = abi.Value

// TypeDesc, PrimitiveDesc, and the rest of the WIT type vocabulary live in
// types.go.

// HostFunc implements a synchronous component import: it receives the lifted
// arguments and returns the lifted results (or an error, which traps the guest
// call). Register it with WithImport.
// HostFunc implements a synchronous component import.
type HostFunc = instance.HostFunc

// AsyncHostFunc implements an async-lowered component import. It receives the
// lifted arguments and an *AsyncCall used to deliver the result -- synchronously
// (call.Resolve before returning) or later, from any goroutine, once the
// call was started via Instance.CallAsync. Register it with WithAsyncImport.
// AsyncHostFunc implements an async-lowered component import.
type AsyncHostFunc = instance.AsyncHostFunc

// AsyncCall is the completion handle an AsyncHostFunc receives. Call Resolve
// with the import's results (or ResolveCancelled). Under CallAsync, Resolve may
// be called from another goroutine after the AsyncHostFunc returns -- that is
// how external I/O completions drive a component forward.
// AsyncCall is the completion handle supplied to an AsyncHostFunc.
type AsyncCall = instance.AsyncCall

// WithImport registers fn as the component's synchronous import iface/name, with
// the given WIT param/result types. iface is the interface name (e.g.
// "wasi:cli/environment") or "" for a top-level import; name is the function
// (or "" for a bare top-level func import).
// WithImport registers a synchronous component import.
func WithImport(iface, name string, fn HostFunc, params, results []TypeDesc) Option {
return instance.WithImport(iface, name, fn, params, results)
}

// WithAsyncImport registers fn as the component's async-lowered import
// iface/name. Pair it with Instance.CallAsync so fn may complete the call later,
// from another goroutine (real I/O), via AsyncCall.Resolve.
// WithAsyncImport registers an async-lowered component import.
func WithAsyncImport(iface, name string, fn AsyncHostFunc, params, results []TypeDesc) Option {
return instance.WithAsyncImport(iface, name, fn, params, results)
}
Loading
Loading