1#![cfg(target_arch = "wasm32")]
7
8use gbp_core::{MemberId, PayloadCodec, StreamType};
9use gbp_node::{Event, GroupNode as RustGroupNode, Sealer};
10use gtp::{GtpAccept, GtpClient as RustGtpClient};
11use js_sys::{Array, Object, Reflect, Uint8Array};
12use std::cell::RefCell;
13use wasm_bindgen::prelude::*;
14
15fn set(obj: &Object, key: &str, val: &JsValue) {
18 Reflect::set(obj, &JsValue::from_str(key), val).unwrap_throw();
19}
20
21fn u8s(bytes: &[u8]) -> JsValue {
22 Uint8Array::from(bytes).into()
23}
24
25fn event_to_js(ev: Event) -> JsValue {
26 let obj = Object::new();
27 match ev {
28 Event::PayloadReceived(p) => {
29 set(&obj, "kind", &"payload_received".into());
30 set(&obj, "streamType", &JsValue::from_f64(p.stream_type.as_u8() as f64));
31 set(&obj, "plaintext", &u8s(&p.plaintext));
32 set(&obj, "sequenceNo", &JsValue::from_f64(p.sequence_no as f64));
33 }
34 Event::StateChanged { from, to } => {
35 set(&obj, "kind", &"state_changed".into());
36 set(&obj, "from", &JsValue::from_str(&from.to_string()));
37 set(&obj, "to", &JsValue::from_str(&to.to_string()));
38 }
39 Event::EpochAdvanced { epoch, transition_id } => {
40 set(&obj, "kind", &"epoch_advanced".into());
41 set(&obj, "epoch", &js_sys::BigInt::from(epoch).into());
42 set(&obj, "transitionId", &JsValue::from_f64(transition_id as f64));
43 }
44 Event::Error { code, reason, fatal, retryable, .. } => {
45 set(&obj, "kind", &"error".into());
46 set(&obj, "code", &JsValue::from_f64(code as f64));
47 set(&obj, "reason", &JsValue::from_str(&reason));
48 set(&obj, "fatal", &JsValue::from_bool(fatal));
49 set(&obj, "retryable", &JsValue::from_bool(retryable));
50 }
51 Event::Control { from, opcode, transition_id, .. } => {
52 set(&obj, "kind", &"control".into());
53 set(&obj, "from", &JsValue::from_f64(from as f64));
54 set(&obj, "opcode", &JsValue::from_f64(opcode as u8 as f64));
55 set(&obj, "transitionId", &JsValue::from_f64(transition_id as f64));
56 }
57 _ => {
58 set(&obj, "kind", &"other".into());
59 }
60 }
61 obj.into()
62}
63
64#[wasm_bindgen]
74pub struct MlsContext {
75 inner: RefCell<gbp_mls::MlsContext>,
76}
77
78#[wasm_bindgen]
79impl MlsContext {
80 #[wasm_bindgen(js_name = "create", static_method_of = MlsContext)]
82 pub fn create(user_id: &str) -> MlsContext {
83 let (ctx, _kpb) = gbp_mls::MlsContext::new_member(user_id.as_bytes())
84 .expect_throw("MlsContext::new_member failed");
85 MlsContext { inner: RefCell::new(ctx) }
86 }
87
88 #[wasm_bindgen(getter)]
90 pub fn epoch(&self) -> u64 {
91 self.inner.borrow().epoch()
92 }
93}
94
95#[wasm_bindgen]
107pub struct GroupNode {
108 inner: RefCell<RustGroupNode>,
109}
110
111#[wasm_bindgen]
112impl GroupNode {
113 #[wasm_bindgen(js_name = "create", static_method_of = GroupNode)]
115 pub fn create(leaf_index: u32, group_id_bytes: &[u8]) -> GroupNode {
116 let gid: [u8; 16] = group_id_bytes.try_into().unwrap_or([0u8; 16]);
117 let node = RustGroupNode::new(leaf_index as MemberId, gid);
118 GroupNode { inner: RefCell::new(node) }
119 }
120
121 #[wasm_bindgen(js_name = "bootstrapAsCreator")]
123 pub fn bootstrap_as_creator(&self, epoch: u64) {
124 self.inner.borrow_mut().bootstrap_as_creator(epoch);
125 }
126
127 #[wasm_bindgen(js_name = "bootstrapAsJoiner")]
129 pub fn bootstrap_as_joiner(&self, epoch: u64, expected_first_tid: u32) {
130 self.inner.borrow_mut().bootstrap_as_joiner(epoch, expected_first_tid);
131 }
132
133 #[wasm_bindgen(js_name = "onWire")]
138 pub fn on_wire(&self, mls: &MlsContext, wire_bytes: &[u8]) -> Array {
139 let mut node = self.inner.borrow_mut();
140 let mut mls_inner = mls.inner.borrow_mut();
141 let events = node.on_wire(&mut *mls_inner, wire_bytes).unwrap_or_default();
142 let arr = Array::new();
143 for ev in events {
144 arr.push(&event_to_js(ev));
145 }
146 arr
147 }
148
149 #[wasm_bindgen(js_name = "checkTimeouts")]
151 pub fn check_timeouts(&self) -> Array {
152 let events = self.inner.borrow_mut().check_timeouts();
153 let arr = Array::new();
154 for ev in events {
155 arr.push(&event_to_js(ev));
156 }
157 arr
158 }
159
160 #[wasm_bindgen(getter, js_name = "lastTransitionId")]
162 pub fn last_transition_id(&self) -> u32 {
163 self.inner.borrow().last_transition_id
164 }
165
166 #[wasm_bindgen(getter, js_name = "currentEpoch")]
168 pub fn current_epoch(&self) -> u64 {
169 self.inner.borrow().current_epoch
170 }
171
172 #[wasm_bindgen(getter, js_name = "memberId")]
174 pub fn member_id(&self) -> u32 {
175 self.inner.borrow().member_id
176 }
177}
178
179#[wasm_bindgen]
194pub struct GtpClient {
195 inner: RefCell<RustGtpClient>,
196}
197
198#[wasm_bindgen]
199impl GtpClient {
200 #[wasm_bindgen(js_name = "create", static_method_of = GtpClient)]
202 pub fn create() -> GtpClient {
203 GtpClient { inner: RefCell::new(RustGtpClient::new()) }
204 }
205
206 #[wasm_bindgen(js_name = "send")]
211 pub fn send(
212 &self,
213 node: &GroupNode,
214 mls: &MlsContext,
215 target: u32,
216 message_id: u64,
217 text: &str,
218 ) -> JsValue {
219 let mut gtp = self.inner.borrow_mut();
220 let mut n = node.inner.borrow_mut();
221 let mut m = mls.inner.borrow_mut();
222 match gtp.send(&mut *n, &mut *m, target as MemberId, message_id, text, PayloadCodec::Cbor) {
223 Ok(frame) => {
224 let obj = Object::new();
225 set(&obj, "wire", &u8s(&frame.wire));
226 set(&obj, "to", &JsValue::from_f64(frame.to as f64));
227 obj.into()
228 }
229 Err(_) => JsValue::NULL,
230 }
231 }
232
233 #[wasm_bindgen(js_name = "accept")]
237 pub fn accept(&self, plaintext: &[u8], epoch: u64) -> JsValue {
238 let mut gtp = self.inner.borrow_mut();
239 match gtp.accept(plaintext, epoch, PayloadCodec::Cbor) {
240 Ok(result) => {
241 let msg = match result {
242 GtpAccept::New(m) | GtpAccept::Duplicate(m) => m,
243 };
244 let text = String::from_utf8_lossy(&msg.content).into_owned();
245 let obj = Object::new();
246 set(&obj, "text", &JsValue::from_str(&text));
247 set(&obj, "messageId", &js_sys::BigInt::from(msg.message_id).into());
248 set(&obj, "senderId", &JsValue::from_f64(msg.sender_id as f64));
249 obj.into()
250 }
251 Err(_) => JsValue::NULL,
252 }
253 }
254
255 #[wasm_bindgen(js_name = "reset")]
257 pub fn reset(&self) {
258 self.inner.borrow_mut().reset();
259 }
260}