bitwarden_ipc/wasm/
ipc_client.rs

1use std::{collections::HashMap, sync::Arc};
2
3use bitwarden_threading::cancellation_token::wasm::{AbortSignal, AbortSignalExt};
4use wasm_bindgen::prelude::*;
5
6use super::communication_backend::JsCommunicationBackend;
7use crate::{
8    ipc_client::{IpcClientSubscription, ReceiveError, SubscribeError},
9    message::{IncomingMessage, OutgoingMessage},
10    traits::{InMemorySessionRepository, NoEncryptionCryptoProvider},
11    IpcClient,
12};
13
14/// JavaScript wrapper around the IPC client. For more information, see the
15/// [IpcClient] documentation.
16#[wasm_bindgen(js_name = IpcClient)]
17pub struct JsIpcClient {
18    #[wasm_bindgen(skip)]
19    /// The underlying IPC client instance. Use this to create WASM-compatible functions
20    /// that interact with the IPC client, e.g. to register RPC handlers, trigger RPC requests,
21    /// send typed messages, etc. For examples see
22    /// [wasm::ipc_register_discover_handler](crate::wasm::ipc_register_discover_handler).
23    pub client: Arc<
24        IpcClient<
25            NoEncryptionCryptoProvider,
26            JsCommunicationBackend,
27            // TODO: Change session provider to a JS-implemented one
28            InMemorySessionRepository<()>,
29        >,
30    >,
31}
32
33/// JavaScript wrapper around the IPC client subscription. For more information, see the
34/// [IpcClientSubscription](crate::IpcClientSubscription) documentation.
35#[wasm_bindgen(js_name = IpcClientSubscription)]
36pub struct JsIpcClientSubscription {
37    subscription: IpcClientSubscription,
38}
39
40#[wasm_bindgen(js_class = IpcClientSubscription)]
41impl JsIpcClientSubscription {
42    #[allow(missing_docs)]
43    pub async fn receive(
44        &mut self,
45        abort_signal: Option<AbortSignal>,
46    ) -> Result<IncomingMessage, ReceiveError> {
47        let cancellation_token = abort_signal.map(|signal| signal.to_cancellation_token());
48        self.subscription.receive(cancellation_token).await
49    }
50}
51
52#[wasm_bindgen(js_class = IpcClient)]
53impl JsIpcClient {
54    #[allow(missing_docs)]
55    #[wasm_bindgen(constructor)]
56    pub fn new(communication_provider: &JsCommunicationBackend) -> JsIpcClient {
57        JsIpcClient {
58            client: IpcClient::new(
59                NoEncryptionCryptoProvider,
60                communication_provider.clone(),
61                InMemorySessionRepository::new(HashMap::new()),
62            ),
63        }
64    }
65
66    #[allow(missing_docs)]
67    pub async fn start(&self) {
68        self.client.start().await
69    }
70
71    #[wasm_bindgen(js_name = isRunning)]
72    #[allow(missing_docs)]
73    pub async fn is_running(&self) -> bool {
74        self.client.is_running().await
75    }
76
77    #[allow(missing_docs)]
78    pub async fn send(&self, message: OutgoingMessage) -> Result<(), JsError> {
79        self.client
80            .send(message)
81            .await
82            .map_err(|e| JsError::new(&e))
83    }
84
85    #[allow(missing_docs)]
86    pub async fn subscribe(&self) -> Result<JsIpcClientSubscription, SubscribeError> {
87        let subscription = self.client.subscribe(None).await?;
88        Ok(JsIpcClientSubscription { subscription })
89    }
90}