feat(sync): add typed acknowledgement effect

req: sync/006
This commit is contained in:
slhx agent
2026-07-13 23:15:57 +02:00
parent 88598f181f
commit 5a82956c08
6 changed files with 163 additions and 7 deletions
+29 -5
View File
@@ -1,4 +1,4 @@
use hemx_core::{Effect, EffectBatch, IntoEffect};
use hemx_core::{Atom, Effect, EffectBatch, IntoEffect};
use serde::{de, Deserialize, Deserializer, Serialize};
use std::{
collections::{HashMap, HashSet},
@@ -12,6 +12,7 @@ pub use hemx_sync_macros::presence;
pub const PATCH_SCHEMA_VERSION: u16 = 1;
pub const PATCH_EVENT: &str = "hemx:sync-patch";
pub const ACK_EVENT: &str = "hemx:sync-ack";
const INTERACTION_ID: &str = "$hemx-interaction";
pub const BROWSER_RUNTIME: &str = include_str!("../runtime/hemx-sync.js");
@@ -387,7 +388,7 @@ fn validate_key(key: &str) -> Result<(), PatchError> {
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SyncEffect(Effect); // req: sync/002
pub struct SyncEffect(Vec<Effect>); // req: sync/002
impl SyncEffect {
// req: sync/004
@@ -398,18 +399,29 @@ impl SyncEffect {
}
}
// req: sync/006
pub fn ack<T>(atom: Atom<T>) -> Self {
Self(vec![
atom.set("acknowledged"),
Effect::Emit {
name: ACK_EVENT.to_owned(),
payload: format!(r#"{{"atomId":{}}}"#, atom.id().id),
},
])
}
pub fn send_patch(patch: FlatPatch) -> Self {
patch.validate().expect("FlatPatch must remain valid");
Self(Effect::Emit {
Self(vec![Effect::Emit {
name: PATCH_EVENT.to_owned(),
payload: patch.payload(),
})
}])
}
}
impl IntoEffect for SyncEffect {
fn append_to(self, ops: &mut Vec<Effect>) {
self.0.append_to(ops);
ops.extend(self.0);
}
}
@@ -417,6 +429,18 @@ impl IntoEffect for SyncEffect {
mod tests {
use super::*;
#[test]
fn acknowledgement_updates_atom_and_emits_queue_signal() {
let atom = Atom::<String>::new(17);
let batch = SyncEffect::ack(atom).into_batch(hemx_core::BuildFingerprint(3));
assert!(
matches!(&batch.ops[0], Effect::Put { target, payload: hemx_core::Payload::Text(payload) } if target.resource == atom.id() && payload == "acknowledged")
);
assert!(
matches!(&batch.ops[1], Effect::Emit { name, payload } if name == ACK_EVENT && payload == r#"{"atomId":17}"#)
);
}
#[test]
fn presence_macro_projects_an_ordinary_effect_on_its_channel() {
struct Signal(Channel);