bitwarden_ipc/wasm/
js_session_repository.rs

1use bitwarden_threading::ThreadBoundRunner;
2use serde::{Serialize, de::DeserializeOwned};
3use tsify::serde_wasm_bindgen;
4use wasm_bindgen::prelude::*;
5
6use crate::{endpoint::Endpoint, traits::SessionRepository};
7
8#[wasm_bindgen(typescript_custom_section)]
9const TS_CUSTOM_TYPES: &'static str = r#"
10export interface IpcSessionRepository {
11    get(endpoint: Endpoint): Promise<any | undefined>;
12    save(endpoint: Endpoint, session: any): Promise<void>;
13    remove(endpoint: Endpoint): Promise<void>;
14}
15"#;
16
17#[wasm_bindgen]
18extern "C" {
19    /// JavaScript interface for handling outgoing messages from the IPC framework.
20    #[wasm_bindgen(js_name = IpcSessionRepository, typescript_type = "IpcSessionRepository")]
21    pub type RawJsSessionRepository;
22
23    /// Used by the IPC framework to get a session for a specific endpoint.
24    #[wasm_bindgen(catch, method, structural)]
25    pub async fn get(this: &RawJsSessionRepository, endpoint: Endpoint)
26    -> Result<JsValue, JsValue>;
27
28    /// Used by the IPC framework to save a session for a specific endpoint.
29    #[wasm_bindgen(catch, method, structural)]
30    pub async fn save(
31        this: &RawJsSessionRepository,
32        endpoint: Endpoint,
33        session: JsValue,
34    ) -> Result<(), JsValue>;
35
36    /// Used by the IPC framework to remove a session for a specific endpoint.
37    #[wasm_bindgen(catch, method, structural)]
38    pub async fn remove(this: &RawJsSessionRepository, endpoint: Endpoint) -> Result<(), JsValue>;
39}
40
41/// Thread safe JavaScript implementation of the `SessionRepository` trait for IPC sessions.
42pub struct JsSessionRepository(ThreadBoundRunner<RawJsSessionRepository>);
43
44impl JsSessionRepository {
45    /// Creates a new `JsSessionRepository` instance wrapping the raw JavaScript repository.
46    pub fn new(repository: RawJsSessionRepository) -> Self {
47        Self(ThreadBoundRunner::new(repository))
48    }
49}
50
51impl Clone for JsSessionRepository {
52    fn clone(&self) -> Self {
53        Self(self.0.clone())
54    }
55}
56
57impl<Session> SessionRepository<Session> for JsSessionRepository
58where
59    Session: Serialize + DeserializeOwned + Send + Sync + 'static,
60{
61    type GetError = String;
62    type SaveError = String;
63    type RemoveError = String;
64
65    async fn get(&self, endpoint: Endpoint) -> Result<Option<Session>, Self::GetError> {
66        self.0
67            .run_in_thread(move |repo| async move {
68                let js_value = repo.get(endpoint).await.map_err(|e| format!("{e:?}"))?;
69                if js_value.is_undefined() || js_value.is_null() {
70                    return Ok(None);
71                }
72
73                Ok(Some(
74                    serde_wasm_bindgen::from_value(js_value).map_err(|e| e.to_string())?,
75                ))
76            })
77            .await
78            .map_err(|e| e.to_string())?
79    }
80
81    async fn save(&self, endpoint: Endpoint, session: Session) -> Result<(), Self::SaveError> {
82        self.0
83            .run_in_thread(move |repo| async move {
84                let js_value = serde_wasm_bindgen::to_value(&session).map_err(|e| e.to_string())?;
85                repo.save(endpoint, js_value)
86                    .await
87                    .map_err(|e| format!("{e:?}"))
88            })
89            .await
90            .map_err(|e| e.to_string())?
91    }
92
93    async fn remove(&self, endpoint: Endpoint) -> Result<(), Self::RemoveError> {
94        self.0
95            .run_in_thread(move |repo| async move {
96                repo.remove(endpoint).await.map_err(|e| format!("{e:?}"))
97            })
98            .await
99            .map_err(|e| e.to_string())?
100    }
101}