Skip to main content

WASM Component Model

This document defines WIT (WebAssembly Interface Types) interfaces for implementing Morphir backends and extensions as portable, sandboxed WASM components.

Design Principles

  • Strongly Typed IR: Full IR types modeled in WIT, not JSON strings
  • Attributes as Document: Since WIT lacks generics, attributes use our document-value AST type
  • Full Pipeline: Support both frontend (source → IR) and backend (IR → target) flows
  • Multi-Granularity: Support operations at distribution, module, and definition levels
  • Capability-Based: Components declare which interfaces they implement
  • Sandboxed: Components have no implicit access to filesystem or network

Benefits

BenefitDescription
Language-agnosticBackends can be written in Rust, Go, C, AssemblyScript, etc.
SandboxedSecure execution with explicit capability grants
PortableRun anywhere WASM runs (CLI, browser, edge)
Hot-reloadableSwap components without restarting the daemon
Type-safeFull IR types at component boundary

WIT Package Structure

wit/
├── morphir-ir/
│ ├── document.wit # Document AST (for attributes)
│ ├── naming.wit # Name, Path, FQName
│ ├── types.wit # Type expressions
│ ├── values.wit # Value expressions, literals, patterns
│ ├── modules.wit # Module specs and defs
│ ├── packages.wit # Package specs and defs
│ └── distributions.wit # Distribution types
├── morphir-extension/
│ ├── info.wit # Required info interface (all extensions)
│ └── capabilities.wit # Capability query interface
├── morphir-frontend/
│ ├── compiler.wit # Source → IR compilation (basic)
│ ├── streaming.wit # Streaming compilation capability
│ ├── incremental.wit # Incremental compilation capability
│ └── fragment.wit # Fragment/REPL compilation capability
├── morphir-backend/
│ ├── generator.wit # IR → target code generation (basic)
│ ├── streaming.wit # Streaming generation capability
│ ├── incremental.wit # Incremental generation capability
│ ├── validator.wit # Validation interface
│ └── transform.wit # IR → IR transformation interface
├── morphir-vfs/
│ ├── reader.wit # VFS read access
│ ├── writer.wit # VFS write access
│ └── workspace.wit # Workspace management
└── morphir-component/
└── worlds.wit # World definitions

Document Type (document.wit)

package morphir:ir@0.4.0;

/// Document AST for schema-less data and attributes
interface document {
/// Recursive document value (JSON-like AST)
variant document-value {
/// null
doc-null,
/// Boolean
doc-bool(bool),
/// Integer
doc-int(s64),
/// Float
doc-float(float64),
/// String
doc-string(string),
/// Array
doc-array(list<document-value>),
/// Object (list of key-value pairs)
doc-object(list<tuple<string, document-value>>),
}

/// Empty document (convenience)
empty: func() -> document-value;

/// Create object from pairs
object: func(pairs: list<tuple<string, document-value>>) -> document-value;

/// Get field from object
get: func(doc: document-value, key: string) -> option<document-value>;

/// Merge two documents (second wins on conflict)
merge: func(base: document-value, overlay: document-value) -> document-value;
}

Naming Types (naming.wit)

package morphir:ir@0.4.0;

use document.{document-value};

/// Core naming types
interface naming {
/// Type-level attributes (optional metadata, no type info needed)
/// Types don't carry type annotations on themselves
variant type-attributes {
/// No attributes
none,
/// Custom metadata only
meta(document-value),
}

/// A single name segment in kebab-case
/// Examples: "user", "order-id", "get-user-by-email"
type name = string;

/// A path is a list of names representing hierarchy
/// Serialized as slash-separated: "main/domain/users"
type path = string;

/// Module path within a package
type module-path = string;

/// Package path (organization/project)
type package-path = string;

/// Qualified name: module path + local name
/// Format: "module/path#local-name"
record qname {
module-path: module-path,
local-name: name,
}

/// Fully qualified name: package + module + local name
/// Format: "package:module#name"
record fqname {
package-path: package-path,
module-path: module-path,
local-name: name,
}

/// Type variable name
type type-variable = string;

/// Parse FQName from canonical string
/// "my-org/project:domain/users#user" -> FQName
fqname-from-string: func(s: string) -> option<fqname>;

/// Render FQName to canonical string
fqname-to-string: func(fqn: fqname) -> string;
}

Type Expressions (types.wit)

package morphir:ir@0.4.0;

use document.{document-value};
use naming.{type-attributes, name, fqname, type-variable};

/// Type system definitions
interface types {
/// Record field
record field {
field-name: name,
field-type: ir-type,
}

/// Constructor for custom types
record constructor {
ctor-name: name,
args: list<tuple<name, ir-type>>,
}

/// Type expression (uses type-attributes: none or metadata)
variant ir-type {
/// Type variable: `a`, `comparable`
variable(tuple<type-attributes, type-variable>),

/// Reference to named type: `String`, `List a`
reference(tuple<type-attributes, fqname, list<ir-type>>),

/// Tuple: `(Int, String)`
%tuple(tuple<type-attributes, list<ir-type>>),

/// Record: `{ name: String, age: Int }`
record(tuple<type-attributes, list<field>>),

/// Extensible record: `{ a | name: String }`
extensible-record(tuple<type-attributes, type-variable, list<field>>),

/// Function: `Int -> String`
%function(tuple<type-attributes, ir-type, ir-type>),

/// Unit type: `()`
unit(type-attributes),
}

/// Value-level attributes (expressions carry their type)
variant value-attributes {
/// Just the inferred/checked type
typed(ir-type),
/// Type plus custom metadata
typed-with-meta(tuple<ir-type, document-value>),
}

/// Access control
enum access {
public,
private,
}

/// Access-controlled wrapper
record access-controlled-constructors {
access: access,
constructors: list<constructor>,
}

/// Type specification (public interface)
variant type-specification {
/// Type alias visible to consumers
type-alias-specification(tuple<list<type-variable>, ir-type>),

/// Opaque type (no structure visible)
opaque-type-specification(list<type-variable>),

/// Custom type with public constructors
custom-type-specification(tuple<list<type-variable>, list<constructor>>),

/// Derived type with conversion functions
derived-type-specification(tuple<list<type-variable>, derived-type-details>),
}

/// Details for derived types
record derived-type-details {
base-type: ir-type,
from-base-type: fqname,
to-base-type: fqname,
}

/// Hole reason for incomplete types
variant hole-reason {
unresolved-reference(fqname),
deleted-during-refactor(string),
type-mismatch(tuple<string, string>),
}

/// Incompleteness marker
variant incompleteness {
hole(hole-reason),
draft(option<string>),
}

/// Type definition (implementation)
variant type-definition {
/// Custom type (sum type)
custom-type-definition(tuple<list<type-variable>, access-controlled-constructors>),

/// Type alias
type-alias-definition(tuple<list<type-variable>, ir-type>),

/// Incomplete type (v4)
incomplete-type-definition(tuple<list<type-variable>, incompleteness, option<ir-type>>),
}
}

Literals (values.wit - Part 1)

package morphir:ir@0.4.0;

/// Literal values
interface literals {
/// Document value for schema-less data
variant document-value {
doc-null,
doc-bool(bool),
doc-int(s64),
doc-float(float64),
doc-string(string),
doc-array(list<document-value>),
doc-object(list<tuple<string, document-value>>),
}

/// Literal constant values
variant literal {
/// Boolean: true, false
bool-literal(bool),

/// Single character
char-literal(string),

/// Text string
string-literal(string),

/// Integer (includes negatives)
integer-literal(s64),

/// Floating-point
float-literal(float64),

/// Arbitrary-precision decimal (stored as string)
decimal-literal(string),

/// Document literal (schema-less JSON-like)
document-literal(document-value),
}
}

Patterns (values.wit - Part 2)

package morphir:ir@0.4.0;

use naming.{attributes, name, fqname};
use literals.{literal};

/// Pattern matching
interface patterns {
use naming.{type-attributes};

/// Pattern for destructuring and matching
/// Patterns use type-attributes (no type info, just optional metadata)
variant pattern {
/// Wildcard: `_`
wildcard-pattern(type-attributes),

/// As pattern: `x` or `(a, b) as pair`
as-pattern(tuple<type-attributes, pattern, name>),

/// Tuple pattern: `(a, b, c)`
tuple-pattern(tuple<type-attributes, list<pattern>>),

/// Constructor pattern: `Just x`
constructor-pattern(tuple<type-attributes, fqname, list<pattern>>),

/// Empty list: `[]`
empty-list-pattern(type-attributes),

/// Head :: tail: `x :: xs`
head-tail-pattern(tuple<type-attributes, pattern, pattern>),

/// Literal match: `42`, `"hello"`
literal-pattern(tuple<type-attributes, literal>),

/// Unit: `()`
unit-pattern(type-attributes),
}
}

Value Expressions (values.wit - Part 3)

package morphir:ir@0.4.0;

use naming.{name, fqname};
use types.{ir-type, value-attributes, hole-reason, incompleteness};
use literals.{literal};
use patterns.{pattern};

/// Value expressions
interface values {
/// Native operation hint
variant native-hint {
arithmetic,
comparison,
string-op,
collection-op,
platform-specific(string),
}

/// Native operation info
record native-info {
hint: native-hint,
description: option<string>,
}

/// Value expression
/// Values use value-attributes (carry their type, optionally with metadata)
variant value {
// === Literals & Data Construction ===

/// Literal constant
%literal(tuple<value-attributes, literal>),

/// Constructor reference: `Just`
%constructor(tuple<value-attributes, fqname>),

/// Tuple: `(1, "hello")`
%tuple(tuple<value-attributes, list<value>>),

/// List: `[1, 2, 3]`
%list(tuple<value-attributes, list<value>>),

/// Record: `{ name = "Alice" }`
%record(tuple<value-attributes, list<tuple<name, value>>>),

/// Unit: `()`
%unit(value-attributes),

// === References ===

/// Variable: `x`
variable(tuple<value-attributes, name>),

/// Reference to defined value: `List.map`
reference(tuple<value-attributes, fqname>),

// === Field Access ===

/// Field access: `record.field`
field(tuple<value-attributes, value, name>),

/// Field function: `.field`
field-function(tuple<value-attributes, name>),

// === Function Application ===

/// Apply: `f x`
apply(tuple<value-attributes, value, value>),

/// Lambda: `\x -> x + 1`
lambda(tuple<value-attributes, pattern, value>),

// === Let Bindings ===

/// Let: `let x = 1 in x + 1`
let-definition(tuple<value-attributes, name, value-definition-body, value>),

/// Recursive let: `let f = ... g ...; g = ... f ... in ...`
let-recursion(tuple<value-attributes, list<tuple<name, value-definition-body>>, value>),

/// Destructure: `let (a, b) = pair in a + b`
destructure(tuple<value-attributes, pattern, value, value>),

// === Control Flow ===

/// If-then-else
if-then-else(tuple<value-attributes, value, value, value>),

/// Pattern match: `case x of ...`
pattern-match(tuple<value-attributes, value, list<tuple<pattern, value>>>),

// === Record Update ===

/// Update: `{ record | field = new }`
update-record(tuple<value-attributes, value, list<tuple<name, value>>>),

// === Special (v4) ===

/// Incomplete/broken reference
hole(tuple<value-attributes, hole-reason, option<ir-type>>),

/// Native operation
native(tuple<value-attributes, fqname, native-info>),

/// External FFI
external(tuple<value-attributes, string, string>),
}

/// Value definition body
variant value-definition-body {
/// Expression body
expression-body(tuple<list<tuple<name, ir-type>>, ir-type, value>),

/// Native body
native-body(tuple<list<tuple<name, ir-type>>, ir-type, native-info>),

/// External body
external-body(tuple<list<tuple<name, ir-type>>, ir-type, string, string>),

/// Incomplete body (v4)
incomplete-body(tuple<list<tuple<name, ir-type>>, option<ir-type>, incompleteness, option<value>>),
}

/// Value specification (signature only)
record value-specification {
inputs: list<tuple<name, ir-type>>,
output: ir-type,
}

/// Access-controlled value definition
record value-definition {
access: types.access,
body: value-definition-body,
}
}

Modules (modules.wit)

package morphir:ir@0.4.0;

use naming.{name, module-path};
use types.{type-specification, type-definition, access};
use values.{value-specification, value-definition};

/// Module definitions
interface modules {
/// Documentation (opaque string or list of strings)
variant documentation {
single-line(string),
multi-line(list<string>),
}

/// Documented wrapper
record documented-type-spec {
doc: option<documentation>,
value: type-specification,
}

/// Module specification (public interface)
record module-specification {
types: list<tuple<name, documented-type-spec>>,
values: list<tuple<name, value-specification>>,
}

/// Documented type definition with access
record access-controlled-documented-type-def {
access: access,
doc: option<documentation>,
definition: type-definition,
}

/// Documented value definition with access
record access-controlled-documented-value-def {
access: access,
doc: option<documentation>,
definition: value-definition,
}

/// Module definition (implementation)
record module-definition {
types: list<tuple<name, access-controlled-documented-type-def>>,
values: list<tuple<name, access-controlled-documented-value-def>>,
}

/// Access-controlled module definition
record access-controlled-module-definition {
access: access,
definition: module-definition,
}
}

Packages (packages.wit)

package morphir:ir@0.4.0;

use naming.{module-path};
use modules.{module-specification, access-controlled-module-definition};

/// Package definitions
interface packages {
/// Package specification (public interface)
record package-specification {
modules: list<tuple<module-path, module-specification>>,
}

/// Package definition (implementation)
record package-definition {
modules: list<tuple<module-path, access-controlled-module-definition>>,
}
}

Distributions (distributions.wit)

package morphir:ir@0.4.0;

use naming.{name, fqname, package-path};
use modules.{documentation};
use packages.{package-specification, package-definition};

/// Distribution types
interface distributions {
/// Semantic version
record semver {
major: u32,
minor: u32,
patch: u32,
pre-release: option<string>,
build-metadata: option<string>,
}

/// Package info
record package-info {
name: package-path,
version: semver,
}

/// Entry point kind
enum entry-point-kind {
main,
command,
handler,
job,
policy,
}

/// Entry point
record entry-point {
target: fqname,
kind: entry-point-kind,
doc: option<documentation>,
}

/// Library distribution
record library-distribution {
package: package-info,
definition: package-definition,
dependencies: list<tuple<package-path, package-specification>>,
}

/// Specs distribution
record specs-distribution {
package: package-info,
specification: package-specification,
dependencies: list<tuple<package-path, package-specification>>,
}

/// Application distribution
record application-distribution {
package: package-info,
definition: package-definition,
dependencies: list<tuple<package-path, package-definition>>,
entry-points: list<tuple<name, entry-point>>,
}

/// Distribution variant
variant distribution {
library(library-distribution),
specs(specs-distribution),
application(application-distribution),
}
}

Extension Info Interface (info.wit)

Every extension must implement this interface - it's the only required interface:

package morphir:extension@0.4.0;

/// Required interface - all extensions must implement this
interface info {
/// Extension type classification
enum extension-type {
/// Frontend/compiler (source → IR)
frontend,
/// Backend/code generator (IR → target)
codegen,
/// Validator/analyzer
validator,
/// IR transformer (IR → IR)
transformer,
/// General purpose
general,
}

/// Extension metadata
record extension-info {
/// Unique identifier (e.g., "spark-codegen")
id: string,
/// Human-readable name
name: string,
/// Version (semver)
version: string,
/// Description
description: string,
/// Author/maintainer
author: option<string>,
/// Homepage/repository URL
homepage: option<string>,
/// License identifier (SPDX)
license: option<string>,
/// Extension types (can fulfill multiple roles)
types: list<extension-type>,
}

/// Return extension metadata
get-info: func() -> extension-info;

/// Health check - return true if extension is ready
ping: func() -> bool;
}

/// Capability discovery interface
interface capabilities {
use info.{extension-type};

/// Capability identifier (e.g., "codegen/generate-streaming")
type capability-id = string;

/// Capability info
record capability-info {
/// Capability identifier
id: capability-id,
/// Human-readable description
description: string,
/// Whether this capability is available
available: bool,
}

/// Options schema for configurable extensions
record option-schema {
/// Option name
name: string,
/// Option type
option-type: option-type,
/// Default value (as JSON string)
default-value: option<string>,
/// Description
description: string,
/// Whether this option is required
required: bool,
}

/// Option type
enum option-type {
%string,
integer,
float,
boolean,
array,
object,
}

/// Query all capabilities this extension provides
list-capabilities: func() -> list<capability-info>;

/// Check if a specific capability is available
has-capability: func(id: capability-id) -> bool;

/// Get targets this extension supports (for codegen)
get-targets: func() -> list<string>;

/// Get languages this extension supports (for frontend)
get-languages: func() -> list<string>;

/// Get configurable options schema
get-options-schema: func() -> list<option-schema>;
}

Frontend Compiler Interface (compiler.wit)

package morphir:frontend@0.4.0;

use morphir:ir@0.4.0.{
naming.{name, module-path, package-path, fqname},
types.{type-definition},
values.{value-definition},
modules.{module-definition},
packages.{package-definition},
distributions.{distribution, semver},
};

/// Frontend compiler interface (source → IR)
interface compiler {
/// Source language
enum source-language {
elm,
morphir-dsl,
custom,
}

/// Compilation granularity capability
flags compiler-capabilities {
/// Can compile entire workspace
workspace,
/// Can compile single project
project,
/// Can compile single module
module,
/// Can compile individual files
file,
/// Can compile code fragments
fragment,
}

/// Compiler metadata
record compiler-info {
name: string,
description: string,
version: string,
source-language: source-language,
custom-language: option<string>,
capabilities: compiler-capabilities,
}

/// Source file
record source-file {
/// File path (relative to project root)
path: string,
/// File content
content: string,
}

/// Project configuration
record project-config {
/// Project name
name: package-path,
/// Project version
version: semver,
/// Source directory
source-dir: string,
/// Dependencies
dependencies: list<tuple<package-path, semver>>,
/// Custom configuration as document
custom: option<morphir:ir@0.4.0.document.document-value>,
}

/// Workspace configuration
record workspace-config {
/// Workspace root path
root: string,
/// Projects in workspace
projects: list<project-config>,
}

/// Fragment context (for incremental/editor compilation)
record fragment-context {
/// Module this fragment belongs to
module-path: module-path,
/// Imports available in scope
imports: list<fqname>,
/// Local bindings in scope
locals: list<tuple<name, morphir:ir@0.4.0.types.ir-type>>,
}

/// Diagnostic severity
enum severity {
error,
warning,
info,
hint,
}

/// Source location
record source-location {
file: string,
start-line: u32,
start-col: u32,
end-line: u32,
end-col: u32,
}

/// Compiler diagnostic
record diagnostic {
severity: severity,
code: string,
message: string,
location: option<source-location>,
hints: list<string>,
}

/// Compilation result for workspace
variant workspace-result {
ok(list<distribution>),
partial(tuple<list<distribution>, list<diagnostic>>),
failed(list<diagnostic>),
}

/// Compilation result for project
variant project-result {
ok(distribution),
partial(tuple<distribution, list<diagnostic>>),
failed(list<diagnostic>),
}

/// Compilation result for files
variant files-result {
ok(package-definition),
partial(tuple<package-definition, list<diagnostic>>),
failed(list<diagnostic>),
}

/// Compilation result for module
variant module-result {
ok(module-definition),
partial(tuple<module-definition, list<diagnostic>>),
failed(list<diagnostic>),
}

/// Compilation result for fragment
variant fragment-result {
/// Compiled to type definition
type-def(type-definition),
/// Compiled to value definition
value-def(value-definition),
/// Compiled to expression (for REPL)
expression(morphir:ir@0.4.0.values.value),
/// Failed
failed(list<diagnostic>),
}

/// Get compiler metadata
info: func() -> compiler-info;

/// Compile entire workspace to IR
compile-workspace: func(
config: workspace-config,
files: list<source-file>,
) -> workspace-result;

/// Compile single project to IR
compile-project: func(
config: project-config,
files: list<source-file>,
) -> project-result;

/// Compile list of files to IR (incremental)
compile-files: func(
config: project-config,
files: list<source-file>,
/// Existing IR to merge with (for incremental)
existing: option<package-definition>,
) -> files-result;

/// Compile a single module to IR
compile-module: func(
config: project-config,
/// Module path within the project
module-path: module-path,
/// Source files for this module
files: list<source-file>,
/// Existing module to merge with (for incremental)
existing: option<module-definition>,
) -> module-result;

/// Compile a code fragment (for editor/REPL)
compile-fragment: func(
source: string,
context: fragment-context,
) -> fragment-result;

/// Parse without full compilation (for syntax checking)
parse-file: func(
file: source-file,
) -> result<_, list<diagnostic>>;

/// Get completions at position (for editor integration)
completions: func(
file: source-file,
line: u32,
column: u32,
context: fragment-context,
) -> list<completion-item>;

/// Completion item
record completion-item {
label: string,
kind: completion-kind,
detail: option<string>,
insert-text: option<string>,
}

/// Completion kind
enum completion-kind {
%function,
variable,
%type,
%constructor,
module,
keyword,
}
}

Generator Interface (generator.wit)

package morphir:backend@0.4.0;

use morphir:ir@0.4.0.{
naming.{fqname, module-path},
types.{type-definition},
values.{value-definition},
modules.{module-definition},
packages.{package-specification},
distributions.{distribution},
};

/// Code generation interface
interface generator {
/// Target language
enum target-language {
typescript,
scala,
java,
go,
python,
rust,
elm,
custom,
}

/// Granularity level
enum granularity {
distribution,
module,
definition,
}

/// Generator metadata
record generator-info {
name: string,
description: string,
version: string,
target: target-language,
custom-target: option<string>,
supported-granularities: list<granularity>,
}

/// Generation options
record generation-options {
output-dir: string,
indent: option<string>,
source-maps: bool,
custom: option<string>,
}

/// Generated artifact
record artifact {
path: string,
content: string,
source-map: option<string>,
}

/// Diagnostic severity
enum severity {
error,
warning,
info,
hint,
}

/// Source location
record source-location {
uri: string,
start-line: u32,
start-col: u32,
end-line: u32,
end-col: u32,
}

/// Diagnostic message
record diagnostic {
severity: severity,
code: string,
message: string,
location: option<source-location>,
}

/// Generation result
variant generation-result {
/// Success
ok(list<artifact>),
/// Partial success with warnings
degraded(tuple<list<artifact>, list<diagnostic>>),
/// Failure
failed(list<diagnostic>),
}

/// Get generator metadata
info: func() -> generator-info;

/// Generate code for entire distribution
generate-distribution: func(
dist: distribution,
options: generation-options,
) -> generation-result;

/// Generate code for a single module
generate-module: func(
path: module-path,
module: module-definition,
deps: package-specification,
options: generation-options,
) -> generation-result;

/// Generate code for a single type
generate-type: func(
fqn: fqname,
def: type-definition,
deps: package-specification,
options: generation-options,
) -> generation-result;

/// Generate code for a single value
generate-value: func(
fqn: fqname,
def: value-definition,
deps: package-specification,
options: generation-options,
) -> generation-result;
}

Validator Interface (validator.wit)

package morphir:backend@0.4.0;

use morphir:ir@0.4.0.{
naming.{fqname, module-path},
types.{type-definition},
values.{value-definition},
modules.{module-definition},
packages.{package-specification},
distributions.{distribution},
};
use generator.{severity, diagnostic, granularity};

/// Validation interface
interface validator {
/// Validator metadata
record validator-info {
name: string,
description: string,
version: string,
categories: list<string>,
supported-granularities: list<granularity>,
}

/// Validation options
record validation-options {
min-severity: option<severity>,
enabled-rules: list<string>,
disabled-rules: list<string>,
custom: option<string>,
}

/// Validation result
record validation-result {
diagnostics: list<diagnostic>,
passed: bool,
error-count: u32,
warning-count: u32,
}

/// Get validator metadata
info: func() -> validator-info;

/// Validate entire distribution
validate-distribution: func(
dist: distribution,
options: validation-options,
) -> validation-result;

/// Validate a single module
validate-module: func(
path: module-path,
module: module-definition,
deps: package-specification,
options: validation-options,
) -> validation-result;

/// Validate a single type
validate-type: func(
fqn: fqname,
def: type-definition,
deps: package-specification,
options: validation-options,
) -> validation-result;

/// Validate a single value
validate-value: func(
fqn: fqname,
def: value-definition,
deps: package-specification,
options: validation-options,
) -> validation-result;
}

Frontend Streaming Interface (frontend/streaming.wit)

Optional capability for streaming compilation results:

package morphir:frontend@0.4.0;

use compiler.{source-file, project-config, diagnostic};
use morphir:ir@0.4.0.modules.{module-definition};
use morphir:ir@0.4.0.naming.{module-path};

/// Streaming compilation interface (optional capability)
interface streaming {
/// Streaming module result
record module-compile-result {
/// Module path
path: module-path,
/// Compiled module (if successful)
module: option<module-definition>,
/// Diagnostics for this module
diagnostics: list<diagnostic>,
}

/// Compile project with streaming results
/// Returns a stream handle for polling results
compile-streaming: func(
config: project-config,
files: list<source-file>,
) -> stream-handle;

/// Stream handle for polling results
type stream-handle = u64;

/// Poll for next result (non-blocking)
/// Returns none when stream is complete
poll-result: func(handle: stream-handle) -> option<module-compile-result>;

/// Check if stream is complete
is-complete: func(handle: stream-handle) -> bool;

/// Cancel streaming compilation
cancel: func(handle: stream-handle);
}

Frontend Incremental Interface (frontend/incremental.wit)

Optional capability for incremental compilation:

package morphir:frontend@0.4.0;

use compiler.{source-file, project-config, diagnostic};
use morphir:ir@0.4.0.packages.{package-definition};
use morphir:ir@0.4.0.naming.{module-path};

/// Incremental compilation interface (optional capability)
interface incremental {
/// File change event
variant file-change {
/// File was created
created(source-file),
/// File was modified
modified(source-file),
/// File was deleted
deleted(string),
/// File was renamed
renamed(tuple<string, string>),
}

/// Incremental compilation result
record incremental-result {
/// Updated package definition
package: package-definition,
/// Modules that were recompiled
recompiled-modules: list<module-path>,
/// Modules that were invalidated (dependents)
invalidated-modules: list<module-path>,
/// Diagnostics from recompilation
diagnostics: list<diagnostic>,
}

/// Apply incremental changes to existing IR
compile-incremental: func(
config: project-config,
/// Existing compiled IR
existing: package-definition,
/// File changes since last compilation
changes: list<file-change>,
) -> incremental-result;

/// Get dependency graph for invalidation analysis
get-module-dependencies: func(
existing: package-definition,
) -> list<tuple<module-path, list<module-path>>>;
}

Backend Streaming Interface (backend/streaming.wit)

Optional capability for streaming code generation:

package morphir:backend@0.4.0;

use generator.{generation-options, artifact, diagnostic};
use morphir:ir@0.4.0.distributions.{distribution};
use morphir:ir@0.4.0.naming.{module-path};

/// Streaming generation interface (optional capability)
interface streaming {
/// Module generation result
record module-generation-result {
/// Module path that was generated
module-path: module-path,
/// Generated artifacts for this module
artifacts: list<artifact>,
/// Diagnostics for this module
diagnostics: list<diagnostic>,
}

/// Generate with streaming results
generate-streaming: func(
dist: distribution,
options: generation-options,
) -> stream-handle;

/// Stream handle for polling results
type stream-handle = u64;

/// Poll for next result (non-blocking)
poll-result: func(handle: stream-handle) -> option<module-generation-result>;

/// Check if stream is complete
is-complete: func(handle: stream-handle) -> bool;

/// Cancel streaming generation
cancel: func(handle: stream-handle);
}

Backend Incremental Interface (backend/incremental.wit)

Optional capability for incremental code generation:

package morphir:backend@0.4.0;

use generator.{generation-options, artifact, diagnostic};
use morphir:ir@0.4.0.distributions.{distribution};
use morphir:ir@0.4.0.naming.{module-path};

/// Incremental generation interface (optional capability)
interface incremental {
/// Module change notification
record module-change {
/// Module path that changed
path: module-path,
/// Type of change
change-type: change-type,
}

/// Change type
enum change-type {
/// Module was added
added,
/// Module was modified
modified,
/// Module was removed
removed,
}

/// Incremental generation result
record incremental-generation-result {
/// Generated/updated artifacts
artifacts: list<artifact>,
/// Artifacts to delete (paths)
deleted-artifacts: list<string>,
/// Diagnostics
diagnostics: list<diagnostic>,
}

/// Generate incrementally for changed modules only
generate-incremental: func(
dist: distribution,
changed-modules: list<module-change>,
options: generation-options,
) -> incremental-generation-result;
}

Transform Interface (backend/transform.wit)

Standalone interface for IR-to-IR transformations:

package morphir:backend@0.4.0;

use generator.{diagnostic};
use morphir:ir@0.4.0.{
naming.{fqname, module-path},
types.{type-definition},
values.{value-definition},
modules.{module-definition},
packages.{package-definition},
distributions.{distribution},
document.{document-value},
};

/// IR transformation interface
interface transform {
/// Transformation metadata
record transform-info {
/// Transform name
name: string,
/// Description
description: string,
/// Version
version: string,
/// Whether transform preserves semantics
semantic-preserving: bool,
}

/// Transformation options
record transform-options {
/// Dry run (report changes without applying)
dry-run: bool,
/// Custom options (JSON-like)
custom: option<document-value>,
}

/// Transformation result
record transform-result {
/// Whether transformation succeeded
success: bool,
/// Number of definitions modified
definitions-modified: u32,
/// Diagnostics (warnings, info)
diagnostics: list<diagnostic>,
}

/// Get transform metadata
info: func() -> transform-info;

/// Transform entire distribution
transform-distribution: func(
dist: distribution,
options: transform-options,
) -> tuple<distribution, transform-result>;

/// Transform a single module
transform-module: func(
path: module-path,
module: module-definition,
options: transform-options,
) -> tuple<module-definition, transform-result>;

/// Transform a single type definition
transform-type: func(
fqn: fqname,
def: type-definition,
options: transform-options,
) -> tuple<type-definition, transform-result>;

/// Transform a single value definition
transform-value: func(
fqn: fqname,
def: value-definition,
options: transform-options,
) -> tuple<value-definition, transform-result>;
}

VFS Interfaces (reader.wit / writer.wit)

package morphir:vfs@0.4.0;

use morphir:ir@0.4.0.{
naming.{fqname, module-path, package-path},
types.{type-definition},
values.{value-definition},
modules.{module-definition},
distributions.{distribution},
};
use morphir:backend@0.4.0.generator.{diagnostic};

/// VFS read-only access
interface reader {
/// Read error
variant read-error {
not-found(string),
permission-denied(string),
parse-error(string),
}

/// Read a type definition
read-type: func(fqn: fqname) -> result<type-definition, read-error>;

/// Read a value definition
read-value: func(fqn: fqname) -> result<value-definition, read-error>;

/// Read a module definition
read-module: func(path: module-path) -> result<module-definition, read-error>;

/// Read entire distribution
read-distribution: func() -> result<distribution, read-error>;

/// List modules in a package
list-modules: func(pkg: package-path) -> result<list<module-path>, read-error>;

/// List types in a module
list-types: func(path: module-path) -> result<list<fqname>, read-error>;

/// List values in a module
list-values: func(path: module-path) -> result<list<fqname>, read-error>;
}

/// VFS write access
interface writer {
/// Write error
variant write-error {
permission-denied(string),
validation-failed(list<diagnostic>),
conflict(string),
}

/// Transaction handle
type transaction = u64;

/// Begin transaction
begin-transaction: func() -> transaction;

/// Commit transaction
commit: func(tx: transaction) -> result<_, write-error>;

/// Rollback transaction
rollback: func(tx: transaction) -> result<_, write-error>;

/// Write a type definition
write-type: func(
tx: transaction,
fqn: fqname,
def: type-definition,
) -> result<_, write-error>;

/// Write a value definition
write-value: func(
tx: transaction,
fqn: fqname,
def: value-definition,
) -> result<_, write-error>;

/// Delete a type
delete-type: func(tx: transaction, fqn: fqname) -> result<_, write-error>;

/// Delete a value
delete-value: func(tx: transaction, fqn: fqname) -> result<_, write-error>;

/// Rename a type
rename-type: func(
tx: transaction,
old-fqn: fqname,
new-fqn: fqname,
) -> result<_, write-error>;

/// Rename a value
rename-value: func(
tx: transaction,
old-fqn: fqname,
new-fqn: fqname,
) -> result<_, write-error>;
}

/// Workspace management
interface workspace {
use morphir:ir@0.4.0.{
naming.{package-path},
distributions.{semver, distribution},
document.{document-value},
};

// ============================================================
// Types
// ============================================================

/// Workspace state
enum workspace-state {
/// Workspace is closed
closed,
/// Workspace is open and ready
open,
/// Workspace is being initialized
initializing,
/// Workspace has errors
error,
}

/// Project state within workspace
enum project-state {
/// Project not yet loaded
unloaded,
/// Project is loading
loading,
/// Project is loaded and ready
ready,
/// Project has compilation errors
error,
/// Project is stale (needs recompilation)
stale,
}

/// Workspace info
record workspace-info {
/// Workspace root path
root: string,
/// Workspace name (derived from root or config)
name: string,
/// Current state
state: workspace-state,
/// Projects in workspace
projects: list<project-info>,
/// Workspace-level configuration
config: option<document-value>,
}

/// Project info
record project-info {
/// Project name (package path)
name: package-path,
/// Project version
version: semver,
/// Project root path (relative to workspace)
path: string,
/// Current state
state: project-state,
/// Source directory
source-dir: string,
/// Dependencies
dependencies: list<dependency-info>,
}

/// Dependency info
record dependency-info {
/// Dependency name
name: package-path,
/// Required version
version: semver,
/// Whether dependency is resolved
resolved: bool,
}

/// Workspace error
variant workspace-error {
/// Workspace not found
not-found(string),
/// Workspace already exists
already-exists(string),
/// Workspace is not open
not-open,
/// Project not found
project-not-found(string),
/// Project already exists
project-already-exists(string),
/// Invalid configuration
invalid-config(string),
/// IO error
io-error(string),
}

/// Watch event type
enum watch-event-type {
/// File created
created,
/// File modified
modified,
/// File deleted
deleted,
/// File renamed
renamed,
}

/// Watch event
record watch-event {
/// Event type
event-type: watch-event-type,
/// Affected path
path: string,
/// New path (for rename events)
new-path: option<string>,
/// Affected project (if determinable)
project: option<package-path>,
}

// ============================================================
// Workspace Lifecycle
// ============================================================

/// Create a new workspace
create-workspace: func(
/// Workspace root path
root: string,
/// Initial configuration
config: option<document-value>,
) -> result<workspace-info, workspace-error>;

/// Open an existing workspace
open-workspace: func(
/// Workspace root path
root: string,
) -> result<workspace-info, workspace-error>;

/// Close the current workspace
close-workspace: func() -> result<_, workspace-error>;

/// Get current workspace info
get-workspace-info: func() -> result<workspace-info, workspace-error>;

/// Update workspace configuration
update-workspace-config: func(
config: document-value,
) -> result<_, workspace-error>;

// ============================================================
// Project Management
// ============================================================

/// Add a project to the workspace
add-project: func(
/// Project name
name: package-path,
/// Project path (relative to workspace root)
path: string,
/// Initial version
version: semver,
/// Source directory
source-dir: string,
) -> result<project-info, workspace-error>;

/// Remove a project from the workspace
remove-project: func(
name: package-path,
) -> result<_, workspace-error>;

/// Get project info
get-project-info: func(
name: package-path,
) -> result<project-info, workspace-error>;

/// List all projects
list-projects: func() -> result<list<project-info>, workspace-error>;

/// Load a project (parse and compile)
load-project: func(
name: package-path,
) -> result<distribution, workspace-error>;

/// Unload a project (free resources)
unload-project: func(
name: package-path,
) -> result<_, workspace-error>;

/// Reload a project (recompile)
reload-project: func(
name: package-path,
) -> result<distribution, workspace-error>;

// ============================================================
// Dependency Management
// ============================================================

/// Add a dependency to a project
add-dependency: func(
project: package-path,
dependency: package-path,
version: semver,
) -> result<_, workspace-error>;

/// Remove a dependency from a project
remove-dependency: func(
project: package-path,
dependency: package-path,
) -> result<_, workspace-error>;

/// Resolve all dependencies for a project
resolve-dependencies: func(
project: package-path,
) -> result<list<dependency-info>, workspace-error>;

/// Resolve all dependencies for entire workspace
resolve-all-dependencies: func() -> result<list<tuple<package-path, list<dependency-info>>>, workspace-error>;

// ============================================================
// File Watching
// ============================================================

/// Start watching workspace for changes
start-watching: func() -> result<_, workspace-error>;

/// Stop watching workspace
stop-watching: func() -> result<_, workspace-error>;

/// Poll for watch events (non-blocking)
poll-events: func() -> list<watch-event>;

// ============================================================
// Workspace Operations
// ============================================================

/// Build all projects in workspace
build-all: func() -> result<list<tuple<package-path, distribution>>, workspace-error>;

/// Clean build artifacts
clean: func(
/// Specific project, or all if none
project: option<package-path>,
) -> result<_, workspace-error>;

/// Get workspace-wide diagnostics
get-diagnostics: func() -> list<tuple<package-path, list<morphir:frontend@0.4.0.compiler.diagnostic>>>;
}

World Definitions (worlds.wit)

package morphir:component@0.4.0;

use morphir:extension@0.4.0.{info, capabilities};
use morphir:frontend@0.4.0.{compiler};
use morphir:frontend@0.4.0 as frontend;
use morphir:backend@0.4.0.{generator, validator, transform};
use morphir:backend@0.4.0 as backend;
use morphir:vfs@0.4.0.{reader, writer, workspace};

// ============================================================
// MINIMAL EXTENSION (Info Only)
// ============================================================

/// Minimal extension - just provides info and health check
/// All extensions should start here before adding capabilities
world info-component {
export info;
}

/// Extension with capability discovery
world discoverable-component {
export info;
export capabilities;
}

// ============================================================
// FRONTEND COMPONENTS (Source → IR)
// ============================================================

/// Minimal frontend compiler component (basic compilation only)
world minimal-compiler-component {
export info;
export compiler;
}

/// Frontend compiler component with capability discovery
world compiler-component {
export info;
export capabilities;
export compiler;
}

/// Frontend with VFS access (for reading dependencies)
world compiler-with-vfs-component {
import reader;
export info;
export compiler;
}

/// Frontend with streaming support
world streaming-compiler-component {
export info;
export capabilities;
export compiler;
export frontend:streaming;
}

/// Frontend with incremental support
world incremental-compiler-component {
import reader;
export info;
export capabilities;
export compiler;
export frontend:incremental;
}

/// Full-featured frontend (all capabilities)
world full-compiler-component {
import reader;
export info;
export capabilities;
export compiler;
export frontend:streaming;
export frontend:incremental;
}

// ============================================================
// BACKEND COMPONENTS (IR → Target)
// ============================================================

/// Minimal code generator component
world minimal-generator-component {
export info;
export generator;
}

/// Code generator component with capability discovery
world generator-component {
export info;
export capabilities;
export generator;
}

/// Generator with streaming support
world streaming-generator-component {
export info;
export capabilities;
export generator;
export backend:streaming;
}

/// Generator with incremental support
world incremental-generator-component {
export info;
export capabilities;
export generator;
export backend:incremental;
}

/// Full-featured generator (all capabilities)
world full-generator-component {
export info;
export capabilities;
export generator;
export backend:streaming;
export backend:incremental;
}

/// Validator component
world validator-component {
export info;
export validator;
}

/// Full backend (generator + validator)
world backend-component {
export info;
export capabilities;
export generator;
export validator;
}

// ============================================================
// TRANSFORMER COMPONENTS (IR → IR)
// ============================================================

/// Standalone transformer (pure IR transformation)
world transform-component {
export info;
export transform;
}

/// Transformer with VFS access
world transformer-component {
import reader;
import writer;
export info;
export capabilities;
export transform;
}

// ============================================================
// WORKSPACE COMPONENTS
// ============================================================

/// Workspace management component (daemon-side)
world workspace-manager-component {
export info;
export workspace;
}

/// Workspace-aware compiler
world workspace-compiler-component {
import workspace;
import reader;
export info;
export capabilities;
export compiler;
}

// ============================================================
// FULL PIPELINE COMPONENTS
// ============================================================

/// Full toolchain component (frontend + backend)
world toolchain-component {
import reader;
export info;
export capabilities;
export compiler;
export generator;
export validator;
}

/// Toolchain with workspace support
world workspace-toolchain-component {
import workspace;
import reader;
import writer;
export info;
export capabilities;
export compiler;
export generator;
export validator;
}

/// Full plugin with all capabilities
world plugin-component {
import reader;
import writer;
import workspace;
export info;
export capabilities;
export compiler;
export generator;
export validator;
export transform;
export frontend:streaming;
export frontend:incremental;
export backend:streaming;
export backend:incremental;
}

Component Manifest

{
"name": "morphir-typescript-backend",
"version": "1.0.0",
"description": "TypeScript code generator for Morphir IR",
"world": "morphir:component/backend-component@0.4.0",
"exports": {
"generator": {
"target": "typescript",
"granularities": ["distribution", "module", "definition"]
},
"validator": {
"categories": ["typescript-compat", "naming"]
}
},
"wasm": {
"path": "morphir-typescript-backend.wasm",
"sha256": "abc123..."
}
}

Security Model

WorldImportsAccess Level
info-componentNoneMetadata only (minimal)
discoverable-componentNoneMetadata + capability query
minimal-compiler-componentNonePure function (source → IR)
compiler-componentNonePure function with capabilities
compiler-with-vfs-componentVFS readerRead-only (for dependencies)
streaming-compiler-componentNoneStreaming compilation
incremental-compiler-componentVFS readerIncremental with dependency graph
full-compiler-componentVFS readerAll frontend capabilities
minimal-generator-componentNonePure function (IR → target)
generator-componentNonePure function with capabilities
streaming-generator-componentNoneStreaming generation
incremental-generator-componentNoneIncremental generation
full-generator-componentNoneAll codegen capabilities
validator-componentNonePure function
backend-componentNoneGenerator + validator
transform-componentNonePure IR transformation
transformer-componentVFS reader, writerScoped to distribution
workspace-manager-componentNone (exports only)Workspace lifecycle management
workspace-compiler-componentWorkspace, VFS readerWorkspace-aware compilation
toolchain-componentVFS readerFull compilation pipeline
workspace-toolchain-componentWorkspace, VFS r/wFull pipeline with workspace
plugin-componentFull VFS + workspaceMaximum access (all capabilities)

Components are sandboxed:

  • No filesystem access outside VFS
  • No network access
  • No environment variables
  • Memory and CPU limits enforced