feat(wasm): validate client event and state ABI
req: client_local/005\nreq: client_local/006\nreq: client_local/007\nreq: client_local/008\nreq: client_local/009\nreq: client_local/010\nreq: client_local/014
This commit is contained in:
+99
-1
@@ -10,6 +10,65 @@ pub use wasm_bindgen::prelude::wasm_bindgen;
|
||||
#[doc(hidden)]
|
||||
pub use wasm_bindgen::*;
|
||||
|
||||
pub const CLIENT_EVENT_ABI_VERSION: u32 = 1;
|
||||
pub const CLIENT_STATE_ABI_VERSION: u32 = 1;
|
||||
|
||||
/// Versioned browser event accepted by client-local handlers.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ClientEvent {
|
||||
pub kind: String,
|
||||
pub value: Option<String>,
|
||||
pub checked: Option<bool>,
|
||||
pub key: Option<String>,
|
||||
}
|
||||
|
||||
/// Explicit root-owned state passed to a client-local handler.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ClientState {
|
||||
pub encoded: String,
|
||||
}
|
||||
|
||||
/// Validates primitive wasm-bindgen values before application code runs.
|
||||
///
|
||||
/// Primitive arguments keep JavaScript from owning a second binary codec. The
|
||||
/// ordinary effect result remains postcard-encoded by `hemx-core`.
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn decode_client_inputs(
|
||||
event_version: u32,
|
||||
kind: String,
|
||||
value: Option<String>,
|
||||
checked: Option<bool>,
|
||||
key: Option<String>,
|
||||
state_version: u32,
|
||||
encoded_state: String,
|
||||
) -> Result<(ClientEvent, ClientState), String> {
|
||||
if event_version != CLIENT_EVENT_ABI_VERSION {
|
||||
return Err(format!(
|
||||
"unsupported client-local event ABI version {event_version}; expected {CLIENT_EVENT_ABI_VERSION}"
|
||||
));
|
||||
}
|
||||
if kind.is_empty() {
|
||||
return Err("invalid client-local event payload: event kind is empty".to_owned());
|
||||
}
|
||||
if state_version != CLIENT_STATE_ABI_VERSION {
|
||||
return Err(format!(
|
||||
"unsupported client-local state ABI version {state_version}; expected {CLIENT_STATE_ABI_VERSION}"
|
||||
));
|
||||
}
|
||||
Ok((
|
||||
ClientEvent {
|
||||
kind,
|
||||
value,
|
||||
checked,
|
||||
key,
|
||||
},
|
||||
ClientState {
|
||||
encoded: encoded_state,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Encodes a client handler result with the ordinary hemx effect wire format.
|
||||
///
|
||||
/// Keeping this conversion here gives generated WASM exports one ABI boundary
|
||||
@@ -21,9 +80,48 @@ pub fn encode_handler_effect(effect: impl IntoEffect, fingerprint: BuildFingerpr
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::encode_handler_effect;
|
||||
use super::{decode_client_inputs, encode_handler_effect, ClientEvent, ClientState};
|
||||
use hemx_core::{BuildFingerprint, EffectBatch, Slot};
|
||||
|
||||
#[test]
|
||||
fn client_inputs_are_typed_and_versioned() {
|
||||
assert_eq!(
|
||||
decode_client_inputs(
|
||||
1,
|
||||
"click".to_owned(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
1,
|
||||
"count=3".to_owned(),
|
||||
),
|
||||
Ok((
|
||||
ClientEvent {
|
||||
kind: "click".to_owned(),
|
||||
value: None,
|
||||
checked: None,
|
||||
key: None,
|
||||
},
|
||||
ClientState {
|
||||
encoded: "count=3".to_owned(),
|
||||
},
|
||||
))
|
||||
); // req: client_local/005 req: client_local/007
|
||||
assert_eq!(
|
||||
decode_client_inputs(
|
||||
1,
|
||||
"click".to_owned(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
2,
|
||||
"count=3".to_owned(),
|
||||
)
|
||||
.expect_err("reject unknown state ABI"),
|
||||
"unsupported client-local state ABI version 2; expected 1"
|
||||
); // req: client_local/008
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn client_handler_uses_the_ordinary_effect_wire_format() {
|
||||
let fingerprint = BuildFingerprint(17);
|
||||
|
||||
Reference in New Issue
Block a user