feat(wit): define resident actor protocol
Add the versioned wasmeld:resident@0.1.0 event/effect contract for timers, streams, datagrams, messages, extension sources, and shutdown. Add a resident-probe Component with locked service and resident WIT dependencies so the ABI can be built, packed, and exercised independently.
This commit is contained in:
Generated
+7
@@ -2980,6 +2980,13 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resident-probe-component"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen 0.41.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ members = [
|
|||||||
"components/spin",
|
"components/spin",
|
||||||
"components/clock-probe",
|
"components/clock-probe",
|
||||||
"components/kv-probe",
|
"components/kv-probe",
|
||||||
|
"components/resident-probe",
|
||||||
"components/wasi-clock-probe",
|
"components/wasi-clock-probe",
|
||||||
]
|
]
|
||||||
default-members = ["crates/wasmeld-runtime"]
|
default-members = ["crates/wasmeld-runtime"]
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "resident-probe-component"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.wasmeld]
|
||||||
|
id = "resident-probe"
|
||||||
|
world = "component:resident-probe/resident-probe-component@0.1.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wit-bindgen.workspace = true
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
mod bindings {
|
||||||
|
wit_bindgen::generate!({
|
||||||
|
path: "wit",
|
||||||
|
world: "resident-probe-component",
|
||||||
|
generate_all,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
use bindings::exports::wasmeld::resident::actor::{
|
||||||
|
DatagramSend, Effect, Event, Guest as ResidentGuest, MessageAck, SourceCommand, StreamWrite,
|
||||||
|
TimerArm,
|
||||||
|
};
|
||||||
|
|
||||||
|
static EVENTS_HANDLED: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
struct ResidentProbe;
|
||||||
|
|
||||||
|
impl bindings::Guest for ResidentProbe {
|
||||||
|
fn init(_config: Vec<u8>) -> Result<(), bindings::ServiceError> {
|
||||||
|
EVENTS_HANDLED.store(0, Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn invoke(_input: Vec<u8>) -> Result<Vec<u8>, bindings::ServiceError> {
|
||||||
|
Ok(EVENTS_HANDLED
|
||||||
|
.load(Ordering::Relaxed)
|
||||||
|
.to_le_bytes()
|
||||||
|
.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResidentGuest for ResidentProbe {
|
||||||
|
fn handle_event(
|
||||||
|
input: Event,
|
||||||
|
) -> Result<Vec<Effect>, bindings::exports::wasmeld::resident::actor::ResidentError> {
|
||||||
|
EVENTS_HANDLED.fetch_add(1, Ordering::Relaxed);
|
||||||
|
Ok(match input {
|
||||||
|
Event::StreamData(chunk) => vec![Effect::WriteStream(StreamWrite {
|
||||||
|
stream_id: chunk.stream_id,
|
||||||
|
bytes: chunk.bytes,
|
||||||
|
})],
|
||||||
|
Event::Datagram(datagram) => vec![Effect::SendDatagram(DatagramSend {
|
||||||
|
endpoint_id: datagram.endpoint_id,
|
||||||
|
peer: datagram.peer,
|
||||||
|
bytes: datagram.bytes,
|
||||||
|
})],
|
||||||
|
Event::Timer(timer) => vec![Effect::ArmTimer(TimerArm {
|
||||||
|
timer_id: timer.timer_id,
|
||||||
|
delay_ms: 10,
|
||||||
|
interval_ms: None,
|
||||||
|
})],
|
||||||
|
Event::Message(message) => vec![Effect::AcknowledgeMessage(MessageAck {
|
||||||
|
subscription_id: message.subscription_id,
|
||||||
|
message_id: message.message_id,
|
||||||
|
})],
|
||||||
|
Event::Source(source) => vec![Effect::SourceCommand(SourceCommand {
|
||||||
|
source_id: source.source_id,
|
||||||
|
command: source.kind,
|
||||||
|
payload: source.payload,
|
||||||
|
})],
|
||||||
|
Event::StreamOpened(_)
|
||||||
|
| Event::StreamWritable(_)
|
||||||
|
| Event::StreamHalfClosed(_)
|
||||||
|
| Event::StreamClosed(_)
|
||||||
|
| Event::Shutdown => Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bindings::export!(ResidentProbe with_types_in bindings);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
schema_version = 1
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
"wasmeld:resident" = "0.1.0"
|
||||||
|
"wasmeld:service" = "0.1.0"
|
||||||
|
|
||||||
|
[replace."wasmeld:resident"]
|
||||||
|
path = "../../wit/resident"
|
||||||
|
|
||||||
|
[replace."wasmeld:service"]
|
||||||
|
path = "../../wit/service"
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
schema_version = 1
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmeld:resident"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "path+../../wit/resident"
|
||||||
|
sha256 = "683a3c7196a6dd77b3f619227aa093430ff6995c097d39bb82d501b7c59a196c"
|
||||||
|
replaced = true
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmeld:service"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "path+../../wit/service"
|
||||||
|
sha256 = "d5497307bbcd1e159f7707f385b488a6f2e26362d5256d5bc1080ac603a51305"
|
||||||
|
replaced = true
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package component:resident-probe@0.1.0;
|
||||||
|
|
||||||
|
world resident-probe-component {
|
||||||
|
include wasmeld:service/service-component@0.1.0;
|
||||||
|
export wasmeld:resident/actor@0.1.0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package wasmeld:resident@0.1.0;
|
||||||
|
|
||||||
|
/// Event/effect ABI for Components that remain active without monopolizing
|
||||||
|
/// their Actor thread. The Host owns timers, subscriptions, sockets, streams,
|
||||||
|
/// and all operating-system handles. A Component handles one bounded event and
|
||||||
|
/// returns effects before control goes back to the Runtime.
|
||||||
|
interface actor {
|
||||||
|
type resource-id = u64;
|
||||||
|
|
||||||
|
record timer-fired {
|
||||||
|
timer-id: resource-id,
|
||||||
|
scheduled-at-ns: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
record stream-opened {
|
||||||
|
endpoint-id: resource-id,
|
||||||
|
stream-id: resource-id,
|
||||||
|
peer: option<string>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record stream-chunk {
|
||||||
|
stream-id: resource-id,
|
||||||
|
bytes: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum close-reason {
|
||||||
|
peer-closed,
|
||||||
|
host-closed,
|
||||||
|
idle-timeout,
|
||||||
|
protocol-error,
|
||||||
|
transport-error,
|
||||||
|
}
|
||||||
|
|
||||||
|
record stream-closed {
|
||||||
|
stream-id: resource-id,
|
||||||
|
reason: close-reason,
|
||||||
|
}
|
||||||
|
|
||||||
|
record datagram {
|
||||||
|
endpoint-id: resource-id,
|
||||||
|
peer: string,
|
||||||
|
bytes: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record message {
|
||||||
|
subscription-id: resource-id,
|
||||||
|
message-id: u64,
|
||||||
|
bytes: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Escape hatch for Host-owned event sources such as filesystem watchers,
|
||||||
|
/// serial devices, process signals, or platform-specific buses. `kind` and
|
||||||
|
/// `payload` are interpreted by the separately versioned Host capability
|
||||||
|
/// that created `source-id`.
|
||||||
|
record source-event {
|
||||||
|
source-id: resource-id,
|
||||||
|
kind: string,
|
||||||
|
payload: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
variant event {
|
||||||
|
timer(timer-fired),
|
||||||
|
stream-opened(stream-opened),
|
||||||
|
stream-data(stream-chunk),
|
||||||
|
stream-writable(resource-id),
|
||||||
|
stream-half-closed(resource-id),
|
||||||
|
stream-closed(stream-closed),
|
||||||
|
datagram(datagram),
|
||||||
|
message(message),
|
||||||
|
source(source-event),
|
||||||
|
shutdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
record stream-write {
|
||||||
|
stream-id: resource-id,
|
||||||
|
bytes: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record datagram-send {
|
||||||
|
endpoint-id: resource-id,
|
||||||
|
peer: string,
|
||||||
|
bytes: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record timer-arm {
|
||||||
|
timer-id: resource-id,
|
||||||
|
delay-ms: u64,
|
||||||
|
interval-ms: option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
record message-ack {
|
||||||
|
subscription-id: resource-id,
|
||||||
|
message-id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
record source-command {
|
||||||
|
source-id: resource-id,
|
||||||
|
command: string,
|
||||||
|
payload: list<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
variant effect {
|
||||||
|
write-stream(stream-write),
|
||||||
|
close-stream(resource-id),
|
||||||
|
pause-stream(resource-id),
|
||||||
|
resume-stream(resource-id),
|
||||||
|
send-datagram(datagram-send),
|
||||||
|
arm-timer(timer-arm),
|
||||||
|
cancel-timer(resource-id),
|
||||||
|
acknowledge-message(message-ack),
|
||||||
|
source-command(source-command),
|
||||||
|
}
|
||||||
|
|
||||||
|
variant resident-error {
|
||||||
|
event-failed(string),
|
||||||
|
}
|
||||||
|
|
||||||
|
handle-event: func(input: event) -> result<list<effect>, resident-error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
world resident-component {
|
||||||
|
export actor;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user