Skip to main content

bitwarden_ipc/wasm/
ipc_client.rs

1use std::{collections::HashMap, sync::Arc};
2
3use bitwarden_threading::cancellation_token::wasm::{AbortSignal, AbortSignalExt};
4use serde::{Deserialize, Serialize};
5use wasm_bindgen::prelude::*;
6
7use super::communication_backend::JsCommunicationBackend;
8use crate::{
9    IpcClientImpl,
10    broadcast::{BroadcastGroup, EndpointSendResult},
11    crypto_provider::noise::crypto_provider::NoiseCryptoProvider,
12    endpoint::Endpoint,
13    error::{AlreadyRunningError, BroadcastError, ReceiveError, SendError, SubscribeError},
14    ipc_client::IpcClientSubscription,
15    ipc_client_trait::IpcClient,
16    message::{IncomingMessage, OutgoingMessage},
17    traits::InMemorySessionRepository,
18    wasm::{
19        JsSessionRepository, RawJsSessionRepository,
20        generic_session_repository::GenericSessionRepository,
21    },
22};
23
24/// JavaScript wrapper around the IPC client. For more information, see the
25/// [`IpcClient`] trait documentation.
26#[bitwarden_ffi::wasm_object(js_name = IpcClient)]
27pub struct JsIpcClient {
28    #[wasm_bindgen(skip)]
29    /// The underlying IPC client instance. Use this to create WASM-compatible functions
30    /// that interact with the IPC client, e.g. to register RPC handlers, trigger RPC requests,
31    /// send typed messages, etc. For examples see
32    /// [wasm::ipc_register_discover_handler](crate::wasm::ipc_register_discover_handler).
33    pub client: Arc<dyn IpcClient>,
34}
35
36/// The outcome of delivering a broadcast message to one member of a [`BroadcastGroup`].
37/// JavaScript counterpart of [`EndpointSendResult`].
38#[derive(Serialize, Deserialize, Debug, Clone)]
39#[bitwarden_ffi::wasm_record]
40pub struct IpcEndpointSendResult {
41    /// The endpoint the message was sent to.
42    pub endpoint: Endpoint,
43    /// Why the send to this endpoint failed. Absent if the send succeeded.
44    pub error: Option<SendError>,
45}
46
47impl From<EndpointSendResult> for IpcEndpointSendResult {
48    fn from(value: EndpointSendResult) -> Self {
49        Self {
50            endpoint: value.endpoint,
51            error: value.result.err(),
52        }
53    }
54}
55
56/// JavaScript wrapper around the IPC client subscription. For more information, see the
57/// [IpcClientSubscription](crate::IpcClientSubscription) documentation.
58#[bitwarden_ffi::wasm_object(js_name = IpcClientSubscription)]
59pub struct JsIpcClientSubscription {
60    subscription: IpcClientSubscription,
61}
62
63#[bitwarden_ffi::wasm_export(js_class = IpcClientSubscription)]
64impl JsIpcClientSubscription {
65    #[wasm_only(
66        note = "Use the `subscribe` method on `IpcClient` to create a subscription instance."
67    )]
68    #[allow(missing_docs)]
69    pub async fn receive(
70        &mut self,
71        abort_signal: Option<AbortSignal>,
72    ) -> Result<IncomingMessage, ReceiveError> {
73        let cancellation_token = abort_signal.map(|signal| signal.to_cancellation_token());
74        self.subscription.receive(cancellation_token).await
75    }
76}
77
78#[bitwarden_ffi::wasm_export(js_class = IpcClient)]
79impl JsIpcClient {
80    /// Create a new `IpcClient` instance with an in-memory session repository for saving
81    /// sessions within the SDK.
82    #[wasm_only]
83    #[wasm_bindgen(js_name = newWithSdkInMemorySessions)]
84    pub fn new_with_sdk_in_memory_sessions(
85        communication_provider: &JsCommunicationBackend,
86    ) -> JsIpcClient {
87        JsIpcClient {
88            client: Arc::new(IpcClientImpl::new(
89                NoiseCryptoProvider::new(),
90                communication_provider.clone(),
91                GenericSessionRepository::InMemory(Arc::new(InMemorySessionRepository::new(
92                    HashMap::new(),
93                ))),
94            )),
95        }
96    }
97
98    /// Create a new `IpcClient` instance with a client-managed session repository for saving
99    /// sessions using State Provider.
100    #[wasm_only]
101    #[wasm_bindgen(js_name = newWithClientManagedSessions)]
102    pub fn new_with_client_managed_sessions(
103        communication_provider: &JsCommunicationBackend,
104        session_repository: RawJsSessionRepository,
105    ) -> JsIpcClient {
106        JsIpcClient {
107            client: Arc::new(IpcClientImpl::new(
108                NoiseCryptoProvider::new(),
109                communication_provider.clone(),
110                GenericSessionRepository::JsSessionRepository(Arc::new(JsSessionRepository::new(
111                    session_repository,
112                ))),
113            )),
114        }
115    }
116
117    #[wasm_only]
118    #[allow(missing_docs)]
119    pub async fn start(
120        &self,
121        abort_signal: Option<AbortSignal>,
122    ) -> Result<(), AlreadyRunningError> {
123        self.client
124            .start(abort_signal.map(|signal| signal.to_cancellation_token()))
125            .await
126    }
127
128    #[wasm_only]
129    #[wasm_bindgen(js_name = isRunning)]
130    #[allow(missing_docs)]
131    pub fn is_running(&self) -> bool {
132        self.client.is_running()
133    }
134
135    #[wasm_only]
136    #[allow(missing_docs)]
137    pub async fn send(&self, message: OutgoingMessage) -> Result<(), JsError> {
138        self.client
139            .send(message)
140            .await
141            .map_err(|e| JsError::new(&e.to_string()))
142    }
143
144    #[wasm_only]
145    #[allow(missing_docs)]
146    pub async fn broadcast(
147        &self,
148        group: BroadcastGroup,
149        payload: Vec<u8>,
150        topic: Option<String>,
151    ) -> Result<Vec<IpcEndpointSendResult>, BroadcastError> {
152        let results = self.client.broadcast(group, payload, topic).await?;
153        Ok(results.into_iter().map(Into::into).collect())
154    }
155
156    #[wasm_only]
157    #[allow(missing_docs)]
158    pub async fn subscribe(&self) -> Result<JsIpcClientSubscription, SubscribeError> {
159        let subscription = self.client.subscribe(None).await?;
160        Ok(JsIpcClientSubscription { subscription })
161    }
162}