spark/lib.rs
1//! Spark — Livewire-equivalent reactive components for Anvilforge.
2//!
3//! Components are server-rendered structs whose state is HMAC-signed (or
4//! AES-256-GCM-encrypted) and embedded in the DOM as a snapshot. The browser
5//! ships the snapshot back on every interaction (`spark:click`, `spark:model`,
6//! …); the server hydrates, dispatches the action, re-renders, and returns
7//! refreshed HTML + a fresh snapshot. The runtime JS morphs the DOM in place.
8//!
9//! Quickstart:
10//!
11//! ```ignore
12//! // app/Spark/Counter.rs
13//! use anvilforge::prelude::*;
14//! use spark::prelude::*;
15//!
16//! #[spark_component(template = "spark/counter")]
17//! pub struct Counter {
18//! pub count: i32,
19//! #[spark(model)] pub draft: String,
20//! }
21//!
22//! #[spark_actions]
23//! impl Counter {
24//! async fn increment(&mut self) -> Result<()> { self.count += 1; Ok(()) }
25//! }
26//! ```
27//!
28//! ```ignore
29//! // bootstrap/app.rs
30//! Application::builder()
31//! .web(spark::install(routes::web::register))
32//! .build();
33//! ```
34
35pub mod broadcast;
36pub mod component;
37pub mod crypto;
38pub mod error;
39pub mod http;
40pub mod install;
41pub mod middleware;
42pub mod morph;
43pub mod prelude;
44pub mod registry;
45pub mod render;
46pub mod request;
47pub mod response;
48pub mod snapshot;
49pub mod template;
50
51pub use broadcast::{broadcast, SparkBroadcast};
52pub use component::{Component, Ctx, MountProps, PropertyWrite};
53pub use error::{Error, Result};
54pub use install::{ensure_bellows_bound, install, install_routes};
55pub use registry::{BoxedComponent, ComponentEntry, DynComponent};
56pub use render::{boot_script, render_mount};
57
58// Re-export for proc-macro consumers: derive macros emit `::spark::serde_json`,
59// `::spark::inventory`, etc. so user crates don't need to add those manually.
60pub use ::async_trait;
61pub use ::futures;
62pub use ::inventory;
63pub use ::serde;
64pub use ::serde_json;
65
66/// Constant-time byte equality, used by snapshot::verify.
67pub fn const_eq(a: &[u8], b: &[u8]) -> bool {
68 if a.len() != b.len() {
69 return false;
70 }
71 let mut diff: u8 = 0;
72 for (x, y) in a.iter().zip(b.iter()) {
73 diff |= x ^ y;
74 }
75 diff == 0
76}