wasm

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package wasm provides WASM compilation, binary scanning, metadata embedding, and component model support for cleat workflows.

It builds WASM modules from Go source, embeds custom sections ("cleat.metadata"), scans binaries for exports and imports, and manages concurrent compilation with build locks.

Key types:

  • BuildConfig — parameters for WASM build directory assembly
  • Metadata — workflow metadata embedded in WASM custom sections
  • ScanResult — exports, imports, and memory info from a WASM binary

Package wasm generates WASM import/export stubs and host adapter code for the cleat workflow transformer.

Supported wasm compilation targets:

  • "go" — Standard Go wasip1 (go build with GOOS=wasip1 GOARCH=wasm)
  • "rust" — Rust via cargo + wasm32-wasip1
  • "java" — Java via Gradle + TeaVM
  • "assemblyscript" — AssemblyScript via asc
  • "python" — Python via componentize-py

Index

Constants

View Source
const CurrentABIVersion = 1

CurrentABIVersion is the ABI version produced by this version of cleat.

View Source
const GoTarget = "go"

GoTarget identifies the standard Go WASM compilation target. Uses GOOS=wasip1 GOARCH=wasm go build.

View Source
const LockFileName = "cleat.lock"

LockFileName is the standard lock file name.

View Source
const LockFileVersion = 2

LockFileVersion is the current cleat.lock schema version.

View Source
const PythonTarget = "python"

PythonTarget identifies the Python WASM compilation target. Used by the Go build system to dispatch to the componentize-py pipeline.

View Source
const RootModulePath = "github.com/cleat-team/cleat"

RootModulePath is the engine module's path. The SDK requires it, so a build directory replacing the SDK with a local checkout must be able to resolve it too -- see where this is used.

View Source
const SDKModulePath = "github.com/cleat-team/cleat/cleat"

SDKModulePath is the guest SDK's module path. It is a constant of the project, not something to derive from whichever module a workflow happens to live in.

Variables

View Source
var IsCleatHostFunction = func(name string) bool {
	return strings.HasPrefix(name, "cleat_") ||
		strings.HasPrefix(name, "set_") ||
		strings.HasPrefix(name, "plugin_") ||
		strings.HasPrefix(name, "schedule_")
}

IsCleatHostFunction returns true if the import name corresponds to a cleat host function import.

View Source
var WitToEnvImport = map[string]map[string]string{
	"cleat:host-calls/durable-call": {
		"durable-call":           "cleat_call",
		"durable-call-retry":     "cleat_call_retry",
		"durable-call-heartbeat": "cleat_call_heartbeat",
	},
	"cleat:host-calls/durable-sleep": {
		"durable-sleep":  "cleat_sleep",
		"durable-now":    "cleat_now",
		"durable-random": "cleat_random",
		"durable-log":    "cleat_log",
	},
	"cleat:host-calls/durable-version": {
		"durable-version":     "cleat_version",
		"durable-min-version": "cleat_min_version",
	},
	"cleat:host-calls/durable-lifecycle": {
		"durable-defer":             "cleat_defer",
		"durable-continue-as-new":   "cleat_continue_as_new",
		"durable-poll-cancellation": "cleat_poll_cancellation",
	},
	"cleat:host-calls/durable-signals": {
		"durable-await-signals":        "cleat_await_signals",
		"durable-poll-signal":          "cleat_poll_signal",
		"durable-send-signal-and-wait": "cleat_send_signal_and_wait",
		"durable-reply-to-signal":      "cleat_reply_to_signal",
		"durable-signal-workflow":      "cleat_signal_workflow",
	},
	"cleat:host-calls/durable-children": {
		"durable-child-workflow":              "cleat_child_workflow",
		"durable-await-child":                 "cleat_await_child",
		"durable-await-all-children":          "cleat_await_all_children",
		"durable-child-workflow-with-options": "cleat_child_workflow_with_options",
	},
	"cleat:host-calls/durable-promises": {
		"durable-create-promise":  "cleat_create_promise",
		"durable-await-promise":   "cleat_await_promise",
		"durable-resolve-promise": "cleat_resolve_promise",
		"durable-reject-promise":  "cleat_reject_promise",
	},
	"cleat:host-calls/durable-state": {
		"set-query-state": "set_query_state",
	},
	"cleat:host-calls/durable-handlers": {
		"durable-register-update-handler": "cleat_register_update_handler",
		"durable-register-query-handler":  "cleat_register_query_handler",
	},
	"cleat:host-calls/durable-messaging": {
		"durable-send":            "cleat_send",
		"durable-schedule-invoke": "cleat_schedule_invoke",
	},
	"cleat:host-calls/durable-identity": {
		"durable-workflow-id": "cleat_workflow_id",
		"durable-run-id":      "cleat_run_id",
	},
	"cleat:host-calls/plugin": {
		"plugin-call":           "plugin_call",
		"plugin-call-streaming": "plugin_call_streaming",
	},
	"cleat:host-calls/durable-lock": {
		"durable-acquire-lock": "cleat_acquire_lock",
		"durable-release-lock": "cleat_release_lock",
	},
	"cleat:host-calls/durable-scope": {
		"set-scope": "cleat_set_scope",
		"get-scope": "cleat_get_scope",
		"uuid":      "cleat_uuid",
	},
	"cleat:host-calls/durable-stream-state": {
		"set-state":    "cleat_set_state",
		"get-state":    "cleat_get_state",
		"delete-state": "cleat_delete_state",
		"incr-state":   "cleat_incr_state",
		"has-state":    "cleat_has_state",
		"list-state":   "cleat_list_state",
	},
	"cleat:host-calls/durable-extended-lifecycle": {
		"continue-as-new-versioned": "cleat_continue_as_new_versioned",
		"side-effect":               "cleat_side_effect",
	},
	"cleat:host-calls/durable-extended-children": {
		"child-workflow-in-schema": "cleat_child_workflow_in_schema",
	},
	"cleat:host-calls/durable-fetch": {
		"fetch": "cleat_fetch",
	},

	"cleat:host-calls/durable-cron": {
		"durable-schedule-cron": "cleat_schedule_cron",
		"durable-delete-cron":   "cleat_delete_cron",
		"durable-list-crons":    "cleat_list_crons",
	},
}

witToEnvImport maps WIT-style import (module, function) pairs to flat "env" module function names. Used to rewrite decomposed componentize-py core WASM binaries so they link against the existing host function registrations.

The outer key is the WIT module name (the import module in the decomposed core WASM). The inner key is the WIT function name (the import field name). The value is the flat function name to use under the "env" module. WitToEnvImport maps WIT (module, function) pairs to flat "env" function names. Exported for use by the wasmtime backend to register host functions under WIT module names in addition to "env".

Functions

func BuildPythonWasm

func BuildPythonWasm(entry, output string, verbose bool) error

func BuildPythonWasmWithRuntime

func BuildPythonWasmWithRuntime(entry, output, targetRuntime string, verbose bool) error

BuildPythonWasmWithRuntime compiles a Python workflow to WASM, selecting the output format based on targetRuntime:

  • "wasmtime" — Component Model binary (skip decomposition)
  • "wazero" — decomposed core WASM module
  • "" — both formats (default)

func DetectLanguage

func DetectLanguage(wasmBytes []byte) string

DetectLanguage attempts to determine the source language of a WASM binary. It first checks the "cleat.metadata" custom section for an explicit Language field. If absent, it scans the import section for Component Model import patterns (imports with module names starting with "cleat:"). If neither provides a result, "go" is returned as the default.

func FindCleatOrphanedImports

func FindCleatOrphanedImports(wasmBytes []byte, expectedImports map[string]bool) []string

FindCleatOrphanedImports scans a WASM binary and detects imported host functions (from the "env" module) that reference cleat operations but were not predicted by the closure analysis. Returns descriptions of each orphaned import found.

expectedImports is the set of import names that the closure analysis predicted (e.g. "cleat_call", "cleat_sleep").

func FindRepoRoot

func FindRepoRoot(from string) (string, error)

FindRepoRoot walks up from the given directory looking for go.mod to locate the repository root. Returns the absolute path to the directory containing go.mod, or an error if not found.

func GenerateExports

func GenerateExports(pkgName string, result *analyzer.AnalysisResult, target string) []byte

GenerateExports produces the content of gen_wasm_exports.go. When target is "go", complex parameter types are deserialized via encoding/json instead of the manual extractJSONString fallback.

func GenerateHostAdapter

func GenerateHostAdapter(pkgName string, usage *UsageInfo, target string) []byte

GenerateHostAdapter produces the content of gen_host_adapter.go. It generates closure-based HostCallsOptions fields that call through WASM imports. The workflow code calls h.MethodName(...) through the HostCalls interface.

func GenerateImports

func GenerateImports(pkgName string, usage *UsageInfo) []byte

GenerateImports produces the content of gen_wasm_imports.go.

func GenerateMemory

func GenerateMemory(pkgName string) []byte

GenerateMemory produces the content of gen_wasm_memory.go.

func HasImport

func HasImport(wasmBytes []byte, module, name string) bool

HasImport checks whether a WASM binary imports a specific function from a specific module. This is used to detect features like the cleat_poll_work dispatch protocol.

func HasWasiImports

func HasWasiImports(wasmBytes []byte) bool

HasWasiImports scans the WASM binary for wasi_snapshot_preview1 import module.

func NeededEnvImports

func NeededEnvImports(wasmBytes []byte) map[string]bool

NeededEnvImports parses the WASM import section and returns the set of function names imported from the "env" module. Used to skip registration of host functions the module doesn't need.

func PatchEmptyImportModuleName

func PatchEmptyImportModuleName(raw []byte, replacementName string) []byte

PatchEmptyImportModuleName patches core WASM bytecode that has imports with empty ("") module names by replacing them with replacementName. This is needed because wazero rejects imports with empty module names, but component-model core modules (produced by componentize-py) use them for cross-module references that the component runtime resolves via the instance DAG.

Returns the modified WASM bytes (a new allocation if changes were made, or the original slice if no empty module names were found).

func PrepareBuildDir

func PrepareBuildDir(cfg *BuildConfig) error

PrepareBuildDir assembles the build directory: copies user source files, writes generated files, and creates a go.mod for wasip1 compilation.

func ReadMemoryInitialPages

func ReadMemoryInitialPages(wasmBytes []byte) uint32

ReadMemoryInitialPages reads the initial (minimum) memory pages required by a WASM binary. It checks the memory section (section ID 5) for directly defined memories and the import section (section ID 2) for imported memories. Returns 0 if no memory requirement is found.

NOTE: This reads the initial/minimum pages from the WASM declaration, not the maximum. Runtime enforcement via wazero's WithMemoryLimitPages is the actual security boundary. The claim-time check here is an early-advisory filter only.

func RewriteWitImports

func RewriteWitImports(wasmBytes []byte) ([]byte, error)

RewriteWitImports rewrites WIT-style import module names in a core WASM binary to flat "env" module names. This allows componentize-py compiled Python workflows (decomposed to core WASM) to link against the existing host function registrations.

Returns nil if the binary has no WIT-style imports that need rewriting. Returns the modified binary on success, or an error if the binary is invalid.

func ToSnakeCase

func ToSnakeCase(s string) string

func WriteLockFile

func WriteLockFile(dir string, lf *LockFile) error

WriteLockFile writes a cleat.lock file to dir.

func WriteMetadata

func WriteMetadata(wasmBytes []byte, meta *Metadata) ([]byte, error)

WriteMetadata appends (or replaces) the "cleat.metadata" custom section in a WASM binary and returns the modified bytes.

Types

type BuildConfig

type BuildConfig struct {
	// SrcDir is the directory containing the user's source files.
	SrcDir string

	// OutDir is the directory where generated files and the WASM binary
	// are written.
	OutDir string

	// PkgName is the Go package name for the build directory.
	PkgName string

	// ModulePath is the module path from the project's go.mod
	// (e.g., "github.com/cleat-team/cleat").
	ModulePath string

	// ProjectRoot is the absolute path to the project root (where go.mod lives).
	ProjectRoot string

	// GoVersion is the Go version from the project's go.mod (e.g., "1.26").
	GoVersion string

	// Outputs holds the generated file contents.
	Outputs *OutputFiles

	// WASMOutput is the filename for the compiled WASM binary
	// (e.g., "place_order.wasm").
	WASMOutput string

	// Target is the compilation target. Only "go" (standard Go/wasip1) is
	// currently supported.
	Target string

	// XfrmSource, if non-nil, provides transformed source files to write
	// instead of copying original source files. Keyed by filename.
	XfrmSource map[string][]byte
}

BuildConfig holds the parameters for assembling a build directory.

type ComponentBundle

type ComponentBundle struct {
	// Modules are the raw core WASM binaries extracted from the component.
	Modules [][]byte

	// Instances describes how to instantiate each core module instance in the
	// component's instance DAG. The slice index IS the instance index.
	Instances []CoreInstance

	// Exports maps component-level export names (e.g. "handler") to the
	// specific instance and export that provides them.
	Exports map[string]ComponentExport

	// ImportModules lists the module names used in import sections across
	// all core modules (e.g. "env", "wasi_snapshot_preview1", "teavm").
	ImportModules []string
}

ComponentBundle represents a decomposed WASM Component Model binary into its constituent core WASM modules, the instance instantiation DAG, and the top-level exports that expose component entry points.

func ParseComponentBundle

func ParseComponentBundle(wasmBytes []byte) (*ComponentBundle, error)

ParseComponentBundle parses a WASM Component Model binary and returns the decomposed ComponentBundle (core modules, instance DAG, and exports).

type ComponentExport

type ComponentExport struct {
	// Name is the component-level export name (e.g. "handler").
	Name string

	// Kind is the export kind: 0=func, 1=table, 2=memory, 3=global.
	Kind byte

	// InstanceIndex is the component instance index that provides this export.
	InstanceIndex int

	// ExportIndex is the index of the export within the instance's module.
	ExportIndex int
}

ComponentExport maps a top-level component export to a specific instance export.

type CoreInstance

type CoreInstance struct {
	// ModuleIndex is the index into ComponentBundle.Modules. -1 means this
	// instance has no module of its own (FromExports-only alias).
	ModuleIndex int

	// Args maps each import module name that the module's WASM import section
	// declares to the source instance that supplies it.
	Args []InstantiateArg

	// FromExports lists export aliases that re-export exports from other
	// instances under different names.
	FromExports []ExportSpec
}

CoreInstance describes how a single core module instance is created in the component's instance DAG.

type ExportSpec

type ExportSpec struct {
	// Name is the export name in THIS instance (the alias).
	Name string

	// Kind is the export kind: 0=func, 1=table, 2=memory, 3=global.
	Kind byte

	// Index is the position within this instance's module's exports
	// (unused for FromExports-only instances).
	Index int

	// SourceInstance is the index of the source instance.
	SourceInstance int

	// SourceName is the export name in the source instance.
	SourceName string
}

ExportSpec describes a single export alias that re-exports an export from another instance under a potentially different name.

type HostFunction

type HostFunction struct {
	ImportName string // snake_case name used in //go:wasmimport
	FieldName  string // the HostCallsOptions field name (e.g., "DurableCall")
}

HostFunction identifies a host function that can be imported from the WASM host environment (e.g., "cleat_call", "cleat_sleep").

type InstantiateArg

type InstantiateArg struct {
	// Name is the import module name the WASM binary uses, e.g. "env",
	// "wasi_snapshot_preview1", or a cross-module name like "libpython3.14.so".
	Name string

	// InstanceIndex is the index of the source instance in
	// ComponentBundle.Instances.
	InstanceIndex int
}

InstantiateArg maps an import module name (as used in the WASM import section) to the source instance that provides those imports.

type LockEntry

type LockEntry struct {
	Version int `json:"version"`
}

LockEntry records a pinned child workflow version.

type LockFile

type LockFile struct {
	Version int                  `json:"version"`
	Policy  string               `json:"policy,omitempty"` // the binding policy used ("frozen", "stable", "latest", "tag:X")
	Entries map[string]LockEntry `json:"entries"`
}

LockFile stores resolved child workflow versions for reproducible builds.

func ReadLockFile

func ReadLockFile(dir string) (*LockFile, error)

ReadLockFile reads and parses a cleat.lock file from dir.

type Metadata

type Metadata struct {
	WorkflowName         string            `json:"workflow_name"`
	WorkflowVersion      int               `json:"workflow_version"`
	ABIVersion           int               `json:"abi_version"`
	MinCompatibleVersion int               `json:"min_compatible_version"`
	PluginDeps           map[string]string `json:"plugin_deps,omitempty"`
	ChildVersions        map[string]int    `json:"child_versions,omitempty"`
	ChildBindingPolicy   string            `json:"child_binding_policy,omitempty"` // deployment channel / binding policy
	Language             string            `json:"language,omitempty"`
}

Metadata is embedded in the "cleat.metadata" custom section of compiled WASM binaries. It carries the information needed for deployment.

func ReadMetadata

func ReadMetadata(wasmBytes []byte) (*Metadata, error)

ReadMetadata extracts the "cleat.metadata" custom section from a WASM binary and unmarshals it into a Metadata struct.

func (*Metadata) EffectivePolicy

func (m *Metadata) EffectivePolicy() string

EffectivePolicy returns the effective child binding policy after applying defaults:

  • "" (empty) — backwards compatible: if ChildVersions populated -> "frozen", else -> "latest"
  • "frozen" — strictly use pinned ChildVersions, never resolve at runtime
  • "stable" — resolve to version with "stable" tag at child creation time
  • "latest" — always resolve to MAX(version) at runtime
  • "tag:X" — resolve to version with tag X (e.g. "tag:canary", "tag:experiment-b")

func (*Metadata) Validate

func (m *Metadata) Validate() error

Validate checks that the metadata fields are within acceptable ranges.

type OutputFiles

type OutputFiles struct {
	Imports string // gen_wasm_imports.go
	Memory  string // gen_wasm_memory.go
	Adapter string // gen_host_adapter.go
	Exports string // gen_wasm_exports.go
}

OutputFiles holds the content of generated files keyed by filename.

func BuildOutputs

func BuildOutputs(pkgName string, usage *UsageInfo, result *analyzer.AnalysisResult, target string) *OutputFiles

BuildOutputs generates all output files for a given analysis result and package name. Returns the generated file contents.

type ScannedImport

type ScannedImport struct {
	Module string // import module name (e.g. "env")
	Name   string // import field name (e.g. "cleat_call")
	Kind   byte   // 0 = func, 1 = table, 2 = mem, 3 = global
}

ScannedImport represents a single import entry in a WASM binary.

func ScanWasmImports

func ScanWasmImports(wasmBytes []byte) ([]ScannedImport, error)

ScanWasmImports reads a WASM binary and returns all import entries. This is used post-compilation to verify that the set of host functions actually imported matches what the closure analysis predicted.

The WASM binary format is:

  • magic: \0asm (4 bytes)
  • version: 1 (4 bytes, little-endian u32)
  • sections: each with id (1 byte) + size (LEB128 u32) + content

Section 2 = Import section.

type UsageInfo

type UsageInfo struct {
	Used map[string]bool // keyed by ImportName

	// Funcs lists the HostFunction descriptors that are actually used,
	// in a stable order (by ImportName).
	Funcs []HostFunction

	// Children records child workflow names detected in the AST.
	// Keys are the first argument string literals of h.ChildWorkflow(name, ...),
	// h.ChildWorkflowWithOptions(name, ...), and h.ChildWorkflowTyped(name, ...).
	Children map[string]bool
}

UsageInfo records which host functions are actually called by the cleat closure and provides lookup helpers for code generation.

func AnalyzeUsage

func AnalyzeUsage(result *analyzer.AnalysisResult, cr *closure.Result) *UsageInfo

AnalyzeUsage scans every function in the cleat closure and returns which HostCalls methods are called.

func (*UsageInfo) Count

func (u *UsageInfo) Count() int

Count returns the number of used host functions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL