From 93c0433ba8dbea1c606756b3ae1f1016aeaa7743 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Tue, 1 Sep 2026 07:31:56 -0400 Subject: [PATCH 1/4] Convert component resource to a record Resources are not composable from unique sources within the same component. For example, component resources in the wac-loader could only be consumed as resources from the wac-loader. This makes it impossible to actually bootstrap and compose a component. Switching to a record avoids these issues. A significant downside to using a record is that the bytes for the component must be initialized when the record is created. This makes lazy loading components on demand impossible. Likewise, if we wanted to change the list to stream, it could only be read once, and inherently less useful. Signed-off-by: Scott Andrews --- components/extract-wit/src/lib.rs | 4 +- components/wac-loader/src/lib.rs | 38 ++++--------------- components/wasm-loader/src/lib.rs | 27 +------------ .../package.wit | 8 ++-- wit/loader.wit | 2 +- wit/types.wit | 4 +- wit/wit.wit | 3 +- 7 files changed, 19 insertions(+), 67 deletions(-) diff --git a/components/extract-wit/src/lib.rs b/components/extract-wit/src/lib.rs index 15067a2..2ce4e86 100644 --- a/components/extract-wit/src/lib.rs +++ b/components/extract-wit/src/lib.rs @@ -19,8 +19,8 @@ impl Guest for ExtractWit { type Wit = ExtractedWit; #[allow(async_fn_in_trait)] - async fn extract(component: &Component) -> Result<(Wit, Package), Error> { - let wasm = component.into_wasm(); + async fn extract(component: Component) -> Result<(Wit, Package), Error> { + let wasm = component.bytes; let decoded = wit_component::decode(&wasm)?; let wit = ExtractedWit::new(decoded.resolve()); diff --git a/components/wac-loader/src/lib.rs b/components/wac-loader/src/lib.rs index 8d24a46..a0969de 100644 --- a/components/wac-loader/src/lib.rs +++ b/components/wac-loader/src/lib.rs @@ -3,32 +3,23 @@ use wac_graph::{types::Package, CompositionGraph, EncodeOptions}; use crate::exports::componentized::component::{ - types::{Component, ComponentBorrow, Error, Guest as TypesGuest, GuestComponent}, + types::{Component, Error}, wac_loader::Guest, }; pub(crate) struct WacLoader; -impl TypesGuest for WacLoader { - type Component = WacComponent; -} - impl Guest for WacLoader { #[allow(async_fn_in_trait)] - async fn plug( - socket: ComponentBorrow<'_>, - plugs: Vec>, - ) -> Result { + async fn plug(socket: Component, plugs: Vec) -> Result { let mut graph = CompositionGraph::new(); - let socket: &WacComponent = socket.get(); - let socket = Package::from_bytes("socket", None, socket.into_wasm(), graph.types_mut())?; + let socket = Package::from_bytes("socket", None, socket.bytes, graph.types_mut())?; let socket = graph.register_package(socket)?; let mut graph_plugs = Vec::new(); for plug in plugs { - let plug: &WacComponent = plug.get(); - let plug = Package::from_bytes("plug", None, plug.into_wasm(), graph.types_mut())?; + let plug = Package::from_bytes("plug", None, plug.bytes, graph.types_mut())?; let plug = graph.register_package(plug)?; graph_plugs.push(plug); } @@ -36,24 +27,9 @@ impl Guest for WacLoader { wac_graph::plug(&mut graph, graph_plugs, socket)?; let composed_wasm = graph.encode(EncodeOptions::default())?; - Ok(Component::new(WacComponent::new(composed_wasm))) - } -} - -pub(crate) struct WacComponent { - wasm: Vec, -} - -impl WacComponent { - fn new(wasm: Vec) -> Self { - Self { wasm } - } -} - -impl GuestComponent for WacComponent { - #[allow(async_fn_in_trait)] - fn into_wasm(&self) -> Vec { - self.wasm.clone() + Ok(Component { + bytes: composed_wasm, + }) } } diff --git a/components/wasm-loader/src/lib.rs b/components/wasm-loader/src/lib.rs index 720a3ba..4af024d 100644 --- a/components/wasm-loader/src/lib.rs +++ b/components/wasm-loader/src/lib.rs @@ -1,41 +1,18 @@ #![no_main] use crate::{ - exports::componentized::component::types::{ - Component, Error, Guest as TypesGuest, GuestComponent, - }, + exports::componentized::component::types::{Component, Error}, exports::componentized::component::wasm_loader::Guest, }; use wit_bindgen::rt::async_support::StreamReader; pub(crate) struct WasmLoader; -impl TypesGuest for WasmLoader { - type Component = WasmComponent; -} - impl Guest for WasmLoader { #[allow(async_fn_in_trait)] async fn load(wasm: StreamReader) -> Result { let wasm = wasm.collect().await; - Ok(Component::new(WasmComponent::new(wasm))) - } -} - -pub(crate) struct WasmComponent { - wasm: Vec, -} - -impl WasmComponent { - fn new(wasm: Vec) -> Self { - Self { wasm } - } -} - -impl GuestComponent for WasmComponent { - #[allow(async_fn_in_trait)] - fn into_wasm(&self) -> Vec { - self.wasm.clone() + Ok(Component { bytes: wasm }) } } diff --git a/components/wit/deps/componentized-component-0.0.0-0/package.wit b/components/wit/deps/componentized-component-0.0.0-0/package.wit index 6661769..77cf8cb 100644 --- a/components/wit/deps/componentized-component-0.0.0-0/package.wit +++ b/components/wit/deps/componentized-component-0.0.0-0/package.wit @@ -5,8 +5,8 @@ interface types { other(option), } - resource component { - into-wasm: func() -> list; + record component { + bytes: list, } } @@ -43,7 +43,7 @@ interface wasm-directory-loader { interface wac-loader { use types.{component, error}; - plug: async func(socket: borrow, plugs: list>) -> result; + plug: async func(socket: component, plugs: list) -> result; } interface wit { @@ -304,7 +304,7 @@ interface wit { %world: func(id: world-id) -> option<%world>; } - extract: async func(component: borrow) -> result, error>; + extract: async func(component: component) -> result, error>; } world imports { diff --git a/wit/loader.wit b/wit/loader.wit index 7f76c1b..0b5a96b 100644 --- a/wit/loader.wit +++ b/wit/loader.wit @@ -31,5 +31,5 @@ interface wasm-directory-loader { interface wac-loader { use types.{component, error}; - plug: async func(socket: borrow, plugs: list>) -> result; + plug: async func(socket: component, plugs: list) -> result; } \ No newline at end of file diff --git a/wit/types.wit b/wit/types.wit index 250cdef..30aa278 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -6,8 +6,8 @@ interface types { other(option), } - resource component { - into-wasm: func() -> list; + record component { + bytes: list, } } diff --git a/wit/wit.wit b/wit/wit.wit index a939a56..ae3a3e5 100644 --- a/wit/wit.wit +++ b/wit/wit.wit @@ -257,6 +257,5 @@ interface wit { %world: func(id: world-id) -> option<%world>; } - extract: async func(component: borrow) -> result, error>; - + extract: async func(component: component) -> result, error>; } From 48ad7a9afb1bac88086712983b34120bb649cbb2 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Tue, 1 Sep 2026 11:11:12 -0400 Subject: [PATCH 2/4] Replace wit resource with record wit map's are now part of the component model spec. Unfortunately, the key cannot be a complex type like a record, or even a type alias for a supported type. So we're denormalizing the id types from records to strings. Each id take the form of `type:{value}` so that the keys in each map are unique. Signed-off-by: Scott Andrews --- components/extract-wit/src/lib.rs | 77 +++++-------------- .../package.wit | 26 +++---- wit/wit.wit | 26 +++---- 3 files changed, 36 insertions(+), 93 deletions(-) diff --git a/components/extract-wit/src/lib.rs b/components/extract-wit/src/lib.rs index 2ce4e86..e2342ef 100644 --- a/components/extract-wit/src/lib.rs +++ b/components/extract-wit/src/lib.rs @@ -5,71 +5,60 @@ use std::collections::BTreeMap; use crate::{ componentized::component::types::{Component, Error}, exports::componentized::component::wit::{ - Docs, Enum, EnumCase, Flag, Flags, Function, FunctionKind, Guest, GuestWit, Handle, - IncludeName, Interface, InterfaceId, List, Map, Package, PackageId, PackageName, Param, - Record, RecordField, Result as Result_, Stability, Stable, Tuple, Type, TypeDef, - TypeDefKind, TypeId, TypeOwner, Unstable, Variant, VariantCase, Version, VersionIdentifier, - Wit, World, WorldId, WorldInclude, WorldItem, WorldItemInterface, WorldKey, + Docs, Enum, EnumCase, Flag, Flags, Function, FunctionKind, Guest, Handle, IncludeName, + Interface, InterfaceId, List, Map, Package, PackageId, PackageName, Param, Record, + RecordField, Result as Result_, Stability, Stable, Tuple, Type, TypeDef, TypeDefKind, + TypeId, TypeOwner, Unstable, Variant, VariantCase, Version, VersionIdentifier, Wit, World, + WorldId, WorldInclude, WorldItem, WorldItemInterface, WorldKey, }, }; pub(crate) struct ExtractWit; impl Guest for ExtractWit { - type Wit = ExtractedWit; - #[allow(async_fn_in_trait)] async fn extract(component: Component) -> Result<(Wit, Package), Error> { let wasm = component.bytes; let decoded = wit_component::decode(&wasm)?; - let wit = ExtractedWit::new(decoded.resolve()); + let wit = Wit::new(decoded.resolve()); let package = wit - .package(ExtractedWit::package_id(decoded.package())) + .packages + .get(&Wit::package_id(decoded.package())) .expect("decoded package must exist"); - Ok((Wit::new(wit), package)) + Ok((wit.clone(), package.clone())) } } -pub(crate) struct ExtractedWit { - worlds: BTreeMap, - interfaces: BTreeMap, - types: BTreeMap, - packages: BTreeMap, -} - -impl ExtractedWit { +impl Wit { fn new(resolve: &wit_parser::Resolve) -> Self { Self { worlds: resolve.worlds.clone().into_iter().fold( BTreeMap::new(), |mut worlds, (id, world)| { - worlds.insert(Self::world_id(id).world_id, Self::world(world)); + worlds.insert(Self::world_id(id), Self::world(world)); worlds }, ), interfaces: resolve.interfaces.clone().into_iter().fold( BTreeMap::new(), |mut interfaces, (id, interface)| { - interfaces.insert( - Self::interface_id(id).interface_id, - Self::interface(interface), - ); + interfaces.insert(Self::interface_id(id), Self::interface(interface)); interfaces }, ), types: resolve.types.clone().into_iter().fold( BTreeMap::new(), |mut types, (id, type_def)| { - types.insert(Self::type_id(id).type_id, Self::type_def(type_def)); + types.insert(Self::type_id(id), Self::type_def(type_def)); types }, ), packages: resolve.packages.clone().into_iter().fold( BTreeMap::new(), |mut packages, (id, package)| { - packages.insert(Self::package_id(id).package_id, Self::package(package)); + packages.insert(Self::package_id(id), Self::package(package)); packages }, ), @@ -77,9 +66,7 @@ impl ExtractedWit { } fn world_id(id: wit_parser::WorldId) -> WorldId { - WorldId { - world_id: u32::try_from(id.index()).expect("id too large"), - } + WorldId::from(format!("world:{}", id.index())) } fn world(world: wit_parser::World) -> World { @@ -147,9 +134,7 @@ impl ExtractedWit { } fn interface_id(id: wit_parser::InterfaceId) -> InterfaceId { - InterfaceId { - interface_id: u32::try_from(id.index()).expect("id too large"), - } + InterfaceId::from(format!("interface:{}", id.index())) } fn interface(interface: wit_parser::Interface) -> Interface { @@ -205,9 +190,7 @@ impl ExtractedWit { } fn type_id(id: wit_parser::TypeId) -> TypeId { - TypeId { - type_id: u32::try_from(id.index()).expect("id too large"), - } + TypeId::from(format!("type:{}", id.index())) } fn type_(type_: wit_parser::Type) -> Type { @@ -369,9 +352,7 @@ impl ExtractedWit { } fn package_id(id: wit_parser::PackageId) -> PackageId { - PackageId { - package_id: u32::try_from(id.index()).expect("id too large"), - } + PackageId::from(format!("package:{}", id.index())) } fn package(package: wit_parser::Package) -> Package { @@ -444,28 +425,6 @@ impl ExtractedWit { } } -impl GuestWit for ExtractedWit { - #[allow(async_fn_in_trait)] - fn world(&self, id: WorldId) -> Option { - self.worlds.get(&id.world_id).cloned() - } - - #[allow(async_fn_in_trait)] - fn interface(&self, id: InterfaceId) -> Option { - self.interfaces.get(&id.interface_id).cloned() - } - - #[allow(async_fn_in_trait)] - fn type_(&self, id: TypeId) -> Option { - self.types.get(&id.type_id).cloned() - } - - #[allow(async_fn_in_trait)] - fn package(&self, id: PackageId) -> Option { - self.packages.get(&id.package_id).cloned() - } -} - impl From for Error { fn from(value: anyhow::Error) -> Self { Self::Other(Some(value.to_string())) diff --git a/components/wit/deps/componentized-component-0.0.0-0/package.wit b/components/wit/deps/componentized-component-0.0.0-0/package.wit index 77cf8cb..c844d86 100644 --- a/components/wit/deps/componentized-component-0.0.0-0/package.wit +++ b/components/wit/deps/componentized-component-0.0.0-0/package.wit @@ -49,9 +49,7 @@ interface wac-loader { interface wit { use types.{component, error}; - record type-id { - type-id: u32, - } + type type-id = string; variant %type { %bool, @@ -209,13 +207,9 @@ interface wit { external-id: option, } - record interface-id { - interface-id: u32, - } + type interface-id = string; - record world-id { - world-id: u32, - } + type world-id = string; variant type-owner { %world(world-id), @@ -261,9 +255,7 @@ interface wit { %interface(interface-id), } - record package-id { - package-id: u32, - } + type package-id = string; record %interface { name: option, @@ -297,11 +289,11 @@ interface wit { worlds: list>, } - resource wit { - %interface: func(id: interface-id) -> option<%interface>; - %package: func(id: package-id) -> option<%package>; - %type: func(id: type-id) -> option; - %world: func(id: world-id) -> option<%world>; + record wit { + interfaces: map, + packages: map, + types: map, + worlds: map, } extract: async func(component: component) -> result, error>; diff --git a/wit/wit.wit b/wit/wit.wit index ae3a3e5..f5b0714 100644 --- a/wit/wit.wit +++ b/wit/wit.wit @@ -2,9 +2,7 @@ interface wit { use types.{component, error}; - record type-id { - type-id: u32, - } + type type-id = string; variant %type { %bool, @@ -177,9 +175,7 @@ interface wit { numeric(u64), } - record interface-id { - interface-id: u32, - } + type interface-id = string; record %interface { name: option, @@ -190,9 +186,7 @@ interface wit { %package: option, } - record world-id { - world-id: u32, - } + type world-id = string; record %world { name: string, @@ -233,9 +227,7 @@ interface wit { %interface(interface-id), } - record package-id { - package-id: u32, - } + type package-id = string; record %package { name: package-name, @@ -250,11 +242,11 @@ interface wit { version: option, } - resource wit { - %interface: func(id: interface-id) -> option<%interface>; - %package: func(id: package-id) -> option<%package>; - %type: func(id: type-id) -> option; - %world: func(id: world-id) -> option<%world>; + record wit { + interfaces: map, + packages: map, + types: map, + worlds: map, } extract: async func(component: component) -> result, error>; From 5e17d32db33b56154d6ccc2c04311cf6435c03fa Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Tue, 1 Sep 2026 11:27:23 -0400 Subject: [PATCH 3/4] Pass wit on the component record Loaders may extract and populate the wit, or leave it empty until a consumer needs the wit content. Signed-off-by: Scott Andrews --- components/extract-wit/src/lib.rs | 43 +-- components/wac-loader/src/lib.rs | 13 +- components/wasm-loader/src/lib.rs | 5 +- .../package.wit | 92 +++---- components/wit/worlds.wit | 1 + wit/types.wit | 250 +++++++++++++++++ wit/wit.wit | 251 +----------------- 7 files changed, 339 insertions(+), 316 deletions(-) diff --git a/components/extract-wit/src/lib.rs b/components/extract-wit/src/lib.rs index e2342ef..2a7f538 100644 --- a/components/extract-wit/src/lib.rs +++ b/components/extract-wit/src/lib.rs @@ -3,37 +3,34 @@ use std::collections::BTreeMap; use crate::{ - componentized::component::types::{Component, Error}, - exports::componentized::component::wit::{ - Docs, Enum, EnumCase, Flag, Flags, Function, FunctionKind, Guest, Handle, IncludeName, - Interface, InterfaceId, List, Map, Package, PackageId, PackageName, Param, Record, - RecordField, Result as Result_, Stability, Stable, Tuple, Type, TypeDef, TypeDefKind, - TypeId, TypeOwner, Unstable, Variant, VariantCase, Version, VersionIdentifier, Wit, World, - WorldId, WorldInclude, WorldItem, WorldItemInterface, WorldKey, + componentized::component::types::{ + Component, Docs, Enum, EnumCase, Error, Flag, Flags, Function, FunctionKind, Handle, + IncludeName, Interface, InterfaceId, List, Map, Package, PackageId, PackageName, Param, + Record, RecordField, Result as Result_, Stability, Stable, Tuple, Type, TypeDef, + TypeDefKind, TypeId, TypeOwner, Unstable, Variant, VariantCase, Version, VersionIdentifier, + Wit, World, WorldId, WorldInclude, WorldItem, WorldItemInterface, WorldKey, }, + exports::componentized::component::wit::Guest, }; pub(crate) struct ExtractWit; impl Guest for ExtractWit { #[allow(async_fn_in_trait)] - async fn extract(component: Component) -> Result<(Wit, Package), Error> { + async fn extract(component: Component) -> Result { let wasm = component.bytes; let decoded = wit_component::decode(&wasm)?; - let wit = Wit::new(decoded.resolve()); - let package = wit - .packages - .get(&Wit::package_id(decoded.package())) - .expect("decoded package must exist"); - - Ok((wit.clone(), package.clone())) + Wit::new(decoded.resolve(), decoded.package()) } } impl Wit { - fn new(resolve: &wit_parser::Resolve) -> Self { - Self { + fn new( + resolve: &wit_parser::Resolve, + package_id: wit_parser::PackageId, + ) -> Result { + let wit = Self { worlds: resolve.worlds.clone().into_iter().fold( BTreeMap::new(), |mut worlds, (id, world)| { @@ -62,7 +59,19 @@ impl Wit { packages }, ), + + default_package: Some(Self::package_id(package_id)), + }; + + if wit + .packages + .get(&wit.default_package.clone().unwrap()) + .is_none() + { + Err(Error::Other(Some("decoded package must exist".to_string())))?; } + + Ok(wit) } fn world_id(id: wit_parser::WorldId) -> WorldId { diff --git a/components/wac-loader/src/lib.rs b/components/wac-loader/src/lib.rs index a0969de..c15b040 100644 --- a/components/wac-loader/src/lib.rs +++ b/components/wac-loader/src/lib.rs @@ -2,10 +2,11 @@ use wac_graph::{types::Package, CompositionGraph, EncodeOptions}; -use crate::exports::componentized::component::{ +use crate::componentized::component::{ types::{Component, Error}, - wac_loader::Guest, + wit, }; +use crate::exports::componentized::component::wac_loader::Guest; pub(crate) struct WacLoader; @@ -27,9 +28,13 @@ impl Guest for WacLoader { wac_graph::plug(&mut graph, graph_plugs, socket)?; let composed_wasm = graph.encode(EncodeOptions::default())?; - Ok(Component { + let mut component = Component { bytes: composed_wasm, - }) + wit: None, + }; + component.wit = Some(wit::extract(component.clone()).await?); + + Ok(component) } } diff --git a/components/wasm-loader/src/lib.rs b/components/wasm-loader/src/lib.rs index 4af024d..5e7eeaa 100644 --- a/components/wasm-loader/src/lib.rs +++ b/components/wasm-loader/src/lib.rs @@ -12,7 +12,10 @@ impl Guest for WasmLoader { #[allow(async_fn_in_trait)] async fn load(wasm: StreamReader) -> Result { let wasm = wasm.collect().await; - Ok(Component { bytes: wasm }) + Ok(Component { + bytes: wasm, + wit: None, + }) } } diff --git a/components/wit/deps/componentized-component-0.0.0-0/package.wit b/components/wit/deps/componentized-component-0.0.0-0/package.wit index c844d86..cd84e37 100644 --- a/components/wit/deps/componentized-component-0.0.0-0/package.wit +++ b/components/wit/deps/componentized-component-0.0.0-0/package.wit @@ -5,50 +5,6 @@ interface types { other(option), } - record component { - bytes: list, - } -} - -interface wasm-loader { - use types.{component, error}; - - load: async func(wasm: stream) -> result; -} - -interface wat-loader { - use types.{component, error}; - - load: async func(wat: stream) -> result; -} - -interface filesystem-loader { - use types.{component, error}; - - load: async func(path: string) -> result; -} - -interface oci-loader { - use types.{component, error}; - - load: async func(image: string) -> result; -} - -interface wasm-directory-loader { - use types.{component, error}; - - load: async func(manifest-key: string) -> result; -} - -interface wac-loader { - use types.{component, error}; - - plug: async func(socket: component, plugs: list) -> result; -} - -interface wit { - use types.{component, error}; - type type-id = string; variant %type { @@ -294,9 +250,55 @@ interface wit { packages: map, types: map, worlds: map, + default-package: option, } - extract: async func(component: component) -> result, error>; + record component { + bytes: list, + wit: option, + } +} + +interface wasm-loader { + use types.{component, error}; + + load: async func(wasm: stream) -> result; +} + +interface wat-loader { + use types.{component, error}; + + load: async func(wat: stream) -> result; +} + +interface filesystem-loader { + use types.{component, error}; + + load: async func(path: string) -> result; +} + +interface oci-loader { + use types.{component, error}; + + load: async func(image: string) -> result; +} + +interface wasm-directory-loader { + use types.{component, error}; + + load: async func(manifest-key: string) -> result; +} + +interface wac-loader { + use types.{component, error}; + + plug: async func(socket: component, plugs: list) -> result; +} + +interface wit { + use types.{component, error, wit}; + + extract: async func(component: component) -> result; } world imports { diff --git a/components/wit/worlds.wit b/components/wit/worlds.wit index 36d778e..13d1e09 100644 --- a/components/wit/worlds.wit +++ b/components/wit/worlds.wit @@ -6,6 +6,7 @@ world extract-wit { world wac-loader { import componentized:component/types@0.0.0-0; + import componentized:component/wit@0.0.0-0; export componentized:component/types@0.0.0-0; export componentized:component/wac-loader@0.0.0-0; } diff --git a/wit/types.wit b/wit/types.wit index 30aa278..d180f27 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -8,6 +8,256 @@ interface types { record component { bytes: list, + wit: option, + } + + type type-id = string; + + variant %type { + %bool, + %s8, + %s16, + %s32, + %s64, + %u8, + %u16, + %u32, + %u64, + %f32, + %f64, + %char, + %string, + %error-context, + id(type-id), + } + + variant type-def-kind { + %record(%record), + %resource, + handle(handle), + %flags(%flags), + %tuple(%tuple), + %variant(%variant), + %enum(%enum), + %option(%type), + %result(%result), + %list(%list), + %map(%map), + %future(option<%type>), + %stream(option<%type>), + %type(%type), + unknown, + } + + record %record { + fields: list, + } + + record record-field { + name: string, + %type: %type, + docs: docs, + } + + variant handle { + %own(type-id), + %borrow(type-id), + } + + record %flags { + %flags: list, + } + + record flag { + name: string, + docs: docs, + } + + record %tuple { + types: list<%type>, + } + + record %variant { + cases: list, + } + + record variant-case { + name: string, + %type: option<%type>, + docs: docs, + } + + record %enum { + cases: list, + } + + record enum-case { + name: string, + docs: docs, + } + + record %result { + ok: option<%type>, + err: option<%type>, + } + + record %list { + %type: %type, + fixed-length: option, + } + + record %map { + key: %type, + value: %type, + } + + record function { + name: string, + kind: function-kind, + params: list, + %result: option<%type>, + docs: docs, + stability: stability, + external-id: option, + } + + variant function-kind { + freestanding, + async-freestanding, + method(type-id), + async-method(type-id), + %static(type-id), + async-static(type-id), + %constructor(type-id), + } + + record param { + name: string, + %type: %type, + } + + variant type-owner { + %world(world-id), + %interface(interface-id), + none, + } + + record docs { + contents: option, + } + + variant stability { + unknown, + unstable(unstable), + stable(stable), + } + + record unstable { + feature: string, + deprecated: option, + } + + record stable { + since: version, + deprecated: option, + } + + record type-def { + name: option, + kind: type-def-kind, + owner: type-owner, + docs: docs, + stability: stability, + external-id: option, + } + + record version { + major: u64, + minor: u64, + patch: u64, + prerelease: option>, + build-metadata: option>, + } + + variant version-identifier { + %string(string), + numeric(u64), + } + + type interface-id = string; + + record %interface { + name: option, + types: list>, + functions: list>, + docs: docs, + stability: stability, + %package: option, + } + + type world-id = string; + + record %world { + name: string, + imports: list>, + exports: list>, + %package: option, + docs: docs, + stability: stability, + includes: list, + } + + variant world-item { + %interface(world-item-interface), + function(function), + %type(type-id) + } + + record world-item-interface { + id: interface-id, + stability: stability, + external-id: option, + docs: docs, + } + + record world-include { + stability: stability, + id: world-id, + names: list, + } + + record include-name { + name: string, + %as: string, + } + + variant world-key { + name(string), + %interface(interface-id), + } + + type package-id = string; + + record %package { + name: package-name, + docs: docs, + interfaces: list>, + worlds: list>, + } + + record package-name { + namespace: string, + name: string, + version: option, + } + + record wit { + interfaces: map, + packages: map, + types: map, + worlds: map, + + default-package: option, } } diff --git a/wit/wit.wit b/wit/wit.wit index f5b0714..80b987b 100644 --- a/wit/wit.wit +++ b/wit/wit.wit @@ -1,253 +1,6 @@ interface wit { - use types.{component, error}; + use types.{component, error, wit}; - type type-id = string; - - variant %type { - %bool, - %s8, - %s16, - %s32, - %s64, - %u8, - %u16, - %u32, - %u64, - %f32, - %f64, - %char, - %string, - %error-context, - id(type-id), - } - - variant type-def-kind { - %record(%record), - %resource, - handle(handle), - %flags(%flags), - %tuple(%tuple), - %variant(%variant), - %enum(%enum), - %option(%type), - %result(%result), - %list(%list), - %map(%map), - %future(option<%type>), - %stream(option<%type>), - %type(%type), - unknown, - } - - record %record { - fields: list, - } - - record record-field { - name: string, - %type: %type, - docs: docs, - } - - variant handle { - %own(type-id), - %borrow(type-id), - } - - record %flags { - %flags: list, - } - - record flag { - name: string, - docs: docs, - } - - record %tuple { - types: list<%type>, - } - - record %variant { - cases: list, - } - - record variant-case { - name: string, - %type: option<%type>, - docs: docs, - } - - record %enum { - cases: list, - } - - record enum-case { - name: string, - docs: docs, - } - - record %result { - ok: option<%type>, - err: option<%type>, - } - - record %list { - %type: %type, - fixed-length: option, - } - - record %map { - key: %type, - value: %type, - } - - record function { - name: string, - kind: function-kind, - params: list, - %result: option<%type>, - docs: docs, - stability: stability, - external-id: option, - } - - variant function-kind { - freestanding, - async-freestanding, - method(type-id), - async-method(type-id), - %static(type-id), - async-static(type-id), - %constructor(type-id), - } - - record param { - name: string, - %type: %type, - } - - variant type-owner { - %world(world-id), - %interface(interface-id), - none, - } - - record docs { - contents: option, - } - - variant stability { - unknown, - unstable(unstable), - stable(stable), - } - - record unstable { - feature: string, - deprecated: option, - } - - record stable { - since: version, - deprecated: option, - } - - record type-def { - name: option, - kind: type-def-kind, - owner: type-owner, - docs: docs, - stability: stability, - external-id: option, - } - - record version { - major: u64, - minor: u64, - patch: u64, - prerelease: option>, - build-metadata: option>, - } - - variant version-identifier { - %string(string), - numeric(u64), - } - - type interface-id = string; - - record %interface { - name: option, - types: list>, - functions: list>, - docs: docs, - stability: stability, - %package: option, - } - - type world-id = string; - - record %world { - name: string, - imports: list>, - exports: list>, - %package: option, - docs: docs, - stability: stability, - includes: list, - } - - variant world-item { - %interface(world-item-interface), - function(function), - %type(type-id) - } - - record world-item-interface { - id: interface-id, - stability: stability, - external-id: option, - docs: docs, - } - - record world-include { - stability: stability, - id: world-id, - names: list, - } - - record include-name { - name: string, - %as: string, - } - - variant world-key { - name(string), - %interface(interface-id), - } - - type package-id = string; - - record %package { - name: package-name, - docs: docs, - interfaces: list>, - worlds: list>, - } - - record package-name { - namespace: string, - name: string, - version: option, - } - - record wit { - interfaces: map, - packages: map, - types: map, - worlds: map, - } - - extract: async func(component: component) -> result, error>; + extract: async func(component: component) -> result; } From 385963aecdd5ff007e1207922d3e00d8262b5afa Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Thu, 3 Sep 2026 15:08:58 -0400 Subject: [PATCH 4/4] Replace component record with list Move wit types back into the wit interface. Signed-off-by: Scott Andrews --- components/extract-wit/src/lib.rs | 17 +- components/wac-loader/src/lib.rs | 17 +- components/wasm-loader/src/lib.rs | 6 +- .../package.wit | 87 +++--- components/wit/worlds.wit | 2 - wit/types.wit | 254 +----------------- wit/wit.wit | 251 ++++++++++++++++- 7 files changed, 307 insertions(+), 327 deletions(-) diff --git a/components/extract-wit/src/lib.rs b/components/extract-wit/src/lib.rs index 2a7f538..a6724a5 100644 --- a/components/extract-wit/src/lib.rs +++ b/components/extract-wit/src/lib.rs @@ -3,14 +3,14 @@ use std::collections::BTreeMap; use crate::{ - componentized::component::types::{ - Component, Docs, Enum, EnumCase, Error, Flag, Flags, Function, FunctionKind, Handle, - IncludeName, Interface, InterfaceId, List, Map, Package, PackageId, PackageName, Param, - Record, RecordField, Result as Result_, Stability, Stable, Tuple, Type, TypeDef, - TypeDefKind, TypeId, TypeOwner, Unstable, Variant, VariantCase, Version, VersionIdentifier, - Wit, World, WorldId, WorldInclude, WorldItem, WorldItemInterface, WorldKey, + componentized::component::types::{Component, Error}, + exports::componentized::component::wit::{ + Docs, Enum, EnumCase, Flag, Flags, Function, FunctionKind, Guest, Handle, IncludeName, + Interface, InterfaceId, List, Map, Package, PackageId, PackageName, Param, Record, + RecordField, Result as Result_, Stability, Stable, Tuple, Type, TypeDef, TypeDefKind, + TypeId, TypeOwner, Unstable, Variant, VariantCase, Version, VersionIdentifier, Wit, World, + WorldId, WorldInclude, WorldItem, WorldItemInterface, WorldKey, }, - exports::componentized::component::wit::Guest, }; pub(crate) struct ExtractWit; @@ -18,8 +18,7 @@ pub(crate) struct ExtractWit; impl Guest for ExtractWit { #[allow(async_fn_in_trait)] async fn extract(component: Component) -> Result { - let wasm = component.bytes; - let decoded = wit_component::decode(&wasm)?; + let decoded = wit_component::decode(&component)?; Wit::new(decoded.resolve(), decoded.package()) } diff --git a/components/wac-loader/src/lib.rs b/components/wac-loader/src/lib.rs index c15b040..9563a53 100644 --- a/components/wac-loader/src/lib.rs +++ b/components/wac-loader/src/lib.rs @@ -2,11 +2,10 @@ use wac_graph::{types::Package, CompositionGraph, EncodeOptions}; -use crate::componentized::component::{ +use crate::exports::componentized::component::{ types::{Component, Error}, - wit, + wac_loader::Guest, }; -use crate::exports::componentized::component::wac_loader::Guest; pub(crate) struct WacLoader; @@ -15,24 +14,18 @@ impl Guest for WacLoader { async fn plug(socket: Component, plugs: Vec) -> Result { let mut graph = CompositionGraph::new(); - let socket = Package::from_bytes("socket", None, socket.bytes, graph.types_mut())?; + let socket = Package::from_bytes("socket", None, socket, graph.types_mut())?; let socket = graph.register_package(socket)?; let mut graph_plugs = Vec::new(); for plug in plugs { - let plug = Package::from_bytes("plug", None, plug.bytes, graph.types_mut())?; + let plug = Package::from_bytes("plug", None, plug, graph.types_mut())?; let plug = graph.register_package(plug)?; graph_plugs.push(plug); } wac_graph::plug(&mut graph, graph_plugs, socket)?; - let composed_wasm = graph.encode(EncodeOptions::default())?; - - let mut component = Component { - bytes: composed_wasm, - wit: None, - }; - component.wit = Some(wit::extract(component.clone()).await?); + let component = graph.encode(EncodeOptions::default())?; Ok(component) } diff --git a/components/wasm-loader/src/lib.rs b/components/wasm-loader/src/lib.rs index 5e7eeaa..b624a5c 100644 --- a/components/wasm-loader/src/lib.rs +++ b/components/wasm-loader/src/lib.rs @@ -11,11 +11,7 @@ pub(crate) struct WasmLoader; impl Guest for WasmLoader { #[allow(async_fn_in_trait)] async fn load(wasm: StreamReader) -> Result { - let wasm = wasm.collect().await; - Ok(Component { - bytes: wasm, - wit: None, - }) + Ok(wasm.collect().await) } } diff --git a/components/wit/deps/componentized-component-0.0.0-0/package.wit b/components/wit/deps/componentized-component-0.0.0-0/package.wit index cd84e37..bd32726 100644 --- a/components/wit/deps/componentized-component-0.0.0-0/package.wit +++ b/components/wit/deps/componentized-component-0.0.0-0/package.wit @@ -5,6 +5,48 @@ interface types { other(option), } + type component = list; +} + +interface wasm-loader { + use types.{component, error}; + + load: async func(wasm: stream) -> result; +} + +interface wat-loader { + use types.{component, error}; + + load: async func(wat: stream) -> result; +} + +interface filesystem-loader { + use types.{component, error}; + + load: async func(path: string) -> result; +} + +interface oci-loader { + use types.{component, error}; + + load: async func(image: string) -> result; +} + +interface wasm-directory-loader { + use types.{component, error}; + + load: async func(manifest-key: string) -> result; +} + +interface wac-loader { + use types.{component, error}; + + plug: async func(socket: component, plugs: list) -> result; +} + +interface wit { + use types.{component, error}; + type type-id = string; variant %type { @@ -253,51 +295,6 @@ interface types { default-package: option, } - record component { - bytes: list, - wit: option, - } -} - -interface wasm-loader { - use types.{component, error}; - - load: async func(wasm: stream) -> result; -} - -interface wat-loader { - use types.{component, error}; - - load: async func(wat: stream) -> result; -} - -interface filesystem-loader { - use types.{component, error}; - - load: async func(path: string) -> result; -} - -interface oci-loader { - use types.{component, error}; - - load: async func(image: string) -> result; -} - -interface wasm-directory-loader { - use types.{component, error}; - - load: async func(manifest-key: string) -> result; -} - -interface wac-loader { - use types.{component, error}; - - plug: async func(socket: component, plugs: list) -> result; -} - -interface wit { - use types.{component, error, wit}; - extract: async func(component: component) -> result; } diff --git a/components/wit/worlds.wit b/components/wit/worlds.wit index 13d1e09..7e17ec3 100644 --- a/components/wit/worlds.wit +++ b/components/wit/worlds.wit @@ -5,8 +5,6 @@ world extract-wit { } world wac-loader { - import componentized:component/types@0.0.0-0; - import componentized:component/wit@0.0.0-0; export componentized:component/types@0.0.0-0; export componentized:component/wac-loader@0.0.0-0; } diff --git a/wit/types.wit b/wit/types.wit index d180f27..f62c4ad 100644 --- a/wit/types.wit +++ b/wit/types.wit @@ -6,258 +6,6 @@ interface types { other(option), } - record component { - bytes: list, - wit: option, - } - - type type-id = string; - - variant %type { - %bool, - %s8, - %s16, - %s32, - %s64, - %u8, - %u16, - %u32, - %u64, - %f32, - %f64, - %char, - %string, - %error-context, - id(type-id), - } - - variant type-def-kind { - %record(%record), - %resource, - handle(handle), - %flags(%flags), - %tuple(%tuple), - %variant(%variant), - %enum(%enum), - %option(%type), - %result(%result), - %list(%list), - %map(%map), - %future(option<%type>), - %stream(option<%type>), - %type(%type), - unknown, - } - - record %record { - fields: list, - } - - record record-field { - name: string, - %type: %type, - docs: docs, - } - - variant handle { - %own(type-id), - %borrow(type-id), - } - - record %flags { - %flags: list, - } - - record flag { - name: string, - docs: docs, - } - - record %tuple { - types: list<%type>, - } - - record %variant { - cases: list, - } - - record variant-case { - name: string, - %type: option<%type>, - docs: docs, - } - - record %enum { - cases: list, - } - - record enum-case { - name: string, - docs: docs, - } - - record %result { - ok: option<%type>, - err: option<%type>, - } - - record %list { - %type: %type, - fixed-length: option, - } - - record %map { - key: %type, - value: %type, - } - - record function { - name: string, - kind: function-kind, - params: list, - %result: option<%type>, - docs: docs, - stability: stability, - external-id: option, - } - - variant function-kind { - freestanding, - async-freestanding, - method(type-id), - async-method(type-id), - %static(type-id), - async-static(type-id), - %constructor(type-id), - } - - record param { - name: string, - %type: %type, - } - - variant type-owner { - %world(world-id), - %interface(interface-id), - none, - } - - record docs { - contents: option, - } - - variant stability { - unknown, - unstable(unstable), - stable(stable), - } - - record unstable { - feature: string, - deprecated: option, - } - - record stable { - since: version, - deprecated: option, - } - - record type-def { - name: option, - kind: type-def-kind, - owner: type-owner, - docs: docs, - stability: stability, - external-id: option, - } - - record version { - major: u64, - minor: u64, - patch: u64, - prerelease: option>, - build-metadata: option>, - } - - variant version-identifier { - %string(string), - numeric(u64), - } - - type interface-id = string; - - record %interface { - name: option, - types: list>, - functions: list>, - docs: docs, - stability: stability, - %package: option, - } - - type world-id = string; - - record %world { - name: string, - imports: list>, - exports: list>, - %package: option, - docs: docs, - stability: stability, - includes: list, - } - - variant world-item { - %interface(world-item-interface), - function(function), - %type(type-id) - } - - record world-item-interface { - id: interface-id, - stability: stability, - external-id: option, - docs: docs, - } - - record world-include { - stability: stability, - id: world-id, - names: list, - } - - record include-name { - name: string, - %as: string, - } - - variant world-key { - name(string), - %interface(interface-id), - } - - type package-id = string; - - record %package { - name: package-name, - docs: docs, - interfaces: list>, - worlds: list>, - } - - record package-name { - namespace: string, - name: string, - version: option, - } - - record wit { - interfaces: map, - packages: map, - types: map, - worlds: map, - - default-package: option, - } + type component = list; } diff --git a/wit/wit.wit b/wit/wit.wit index 80b987b..226ad1c 100644 --- a/wit/wit.wit +++ b/wit/wit.wit @@ -1,6 +1,255 @@ interface wit { - use types.{component, error, wit}; + use types.{component, error}; + + type type-id = string; + + variant %type { + %bool, + %s8, + %s16, + %s32, + %s64, + %u8, + %u16, + %u32, + %u64, + %f32, + %f64, + %char, + %string, + %error-context, + id(type-id), + } + + variant type-def-kind { + %record(%record), + %resource, + handle(handle), + %flags(%flags), + %tuple(%tuple), + %variant(%variant), + %enum(%enum), + %option(%type), + %result(%result), + %list(%list), + %map(%map), + %future(option<%type>), + %stream(option<%type>), + %type(%type), + unknown, + } + + record %record { + fields: list, + } + + record record-field { + name: string, + %type: %type, + docs: docs, + } + + variant handle { + %own(type-id), + %borrow(type-id), + } + + record %flags { + %flags: list, + } + + record flag { + name: string, + docs: docs, + } + + record %tuple { + types: list<%type>, + } + + record %variant { + cases: list, + } + + record variant-case { + name: string, + %type: option<%type>, + docs: docs, + } + + record %enum { + cases: list, + } + + record enum-case { + name: string, + docs: docs, + } + + record %result { + ok: option<%type>, + err: option<%type>, + } + + record %list { + %type: %type, + fixed-length: option, + } + + record %map { + key: %type, + value: %type, + } + + record function { + name: string, + kind: function-kind, + params: list, + %result: option<%type>, + docs: docs, + stability: stability, + external-id: option, + } + + variant function-kind { + freestanding, + async-freestanding, + method(type-id), + async-method(type-id), + %static(type-id), + async-static(type-id), + %constructor(type-id), + } + + record param { + name: string, + %type: %type, + } + + variant type-owner { + %world(world-id), + %interface(interface-id), + none, + } + + record docs { + contents: option, + } + + variant stability { + unknown, + unstable(unstable), + stable(stable), + } + + record unstable { + feature: string, + deprecated: option, + } + + record stable { + since: version, + deprecated: option, + } + + record type-def { + name: option, + kind: type-def-kind, + owner: type-owner, + docs: docs, + stability: stability, + external-id: option, + } + + record version { + major: u64, + minor: u64, + patch: u64, + prerelease: option>, + build-metadata: option>, + } + + variant version-identifier { + %string(string), + numeric(u64), + } + + type interface-id = string; + + record %interface { + name: option, + types: list>, + functions: list>, + docs: docs, + stability: stability, + %package: option, + } + + type world-id = string; + + record %world { + name: string, + imports: list>, + exports: list>, + %package: option, + docs: docs, + stability: stability, + includes: list, + } + + variant world-item { + %interface(world-item-interface), + function(function), + %type(type-id) + } + + record world-item-interface { + id: interface-id, + stability: stability, + external-id: option, + docs: docs, + } + + record world-include { + stability: stability, + id: world-id, + names: list, + } + + record include-name { + name: string, + %as: string, + } + + variant world-key { + name(string), + %interface(interface-id), + } + + type package-id = string; + + record %package { + name: package-name, + docs: docs, + interfaces: list>, + worlds: list>, + } + + record package-name { + namespace: string, + name: string, + version: option, + } + + record wit { + interfaces: map, + packages: map, + types: map, + worlds: map, + + default-package: option, + } extract: async func(component: component) -> result; }