bitwarden_server_communication_config/wasm/
js_repository.rs

1use bitwarden_threading::ThreadBoundRunner;
2use wasm_bindgen::prelude::*;
3
4use crate::{ServerCommunicationConfig, ServerCommunicationConfigRepository};
5
6#[wasm_bindgen(typescript_custom_section)]
7const TS_CUSTOM_TYPES: &'static str = r#"
8/**
9 * Repository interface for storing server communication configuration.
10 * 
11 * Implementations use StateProvider (or equivalent storage mechanism) to
12 * persist configuration across sessions. The hostname is typically the vault
13 * server's hostname (e.g., "vault.acme.com").
14 */
15export interface ServerCommunicationConfigRepository {
16    /**
17     * Retrieves the server communication configuration for a given hostname.
18     * 
19     * @param hostname The server hostname (e.g., "vault.acme.com")
20     * @returns The configuration if it exists, undefined otherwise
21     */
22    get(hostname: string): Promise<ServerCommunicationConfig | undefined>;
23    
24    /**
25     * Saves the server communication configuration for a given hostname.
26     * 
27     * @param hostname The server hostname (e.g., "vault.acme.com")
28     * @param config The configuration to store
29     */
30    save(hostname: string, config: ServerCommunicationConfig): Promise<void>;
31}
32"#;
33
34#[wasm_bindgen]
35extern "C" {
36    /// JavaScript interface for the ServerCommunicationConfigRepository
37    #[wasm_bindgen(
38        js_name = ServerCommunicationConfigRepository,
39        typescript_type = "ServerCommunicationConfigRepository"
40    )]
41    pub type RawJsServerCommunicationConfigRepository;
42
43    /// Retrieves configuration for a hostname
44    #[wasm_bindgen(catch, method, structural)]
45    pub async fn get(
46        this: &RawJsServerCommunicationConfigRepository,
47        hostname: String,
48    ) -> Result<JsValue, JsValue>;
49
50    /// Saves configuration for a hostname
51    #[wasm_bindgen(catch, method, structural)]
52    pub async fn save(
53        this: &RawJsServerCommunicationConfigRepository,
54        hostname: String,
55        config: JsValue,
56    ) -> Result<(), JsValue>;
57}
58
59/// Thread-safe JavaScript implementation of ServerCommunicationConfigRepository
60///
61/// This wrapper ensures the JavaScript repository can be safely used across
62/// threads in WASM environments by using ThreadBoundRunner to pin operations
63/// to the main thread.
64pub struct JsServerCommunicationConfigRepository(
65    ThreadBoundRunner<RawJsServerCommunicationConfigRepository>,
66);
67
68impl JsServerCommunicationConfigRepository {
69    /// Creates a new JsServerCommunicationConfigRepository wrapping the raw JavaScript repository
70    pub fn new(repository: RawJsServerCommunicationConfigRepository) -> Self {
71        Self(ThreadBoundRunner::new(repository))
72    }
73}
74
75impl Clone for JsServerCommunicationConfigRepository {
76    fn clone(&self) -> Self {
77        Self(self.0.clone())
78    }
79}
80
81impl ServerCommunicationConfigRepository for JsServerCommunicationConfigRepository {
82    type GetError = String;
83    type SaveError = String;
84
85    async fn get(&self, hostname: String) -> Result<Option<ServerCommunicationConfig>, String> {
86        self.0
87            .run_in_thread(move |repo| async move {
88                let js_value = repo.get(hostname).await.map_err(|e| format!("{e:?}"))?;
89
90                if js_value.is_undefined() || js_value.is_null() {
91                    return Ok(None);
92                }
93
94                Ok(Some(
95                    serde_wasm_bindgen::from_value(js_value).map_err(|e| e.to_string())?,
96                ))
97            })
98            .await
99            .map_err(|e| e.to_string())?
100    }
101
102    async fn save(
103        &self,
104        hostname: String,
105        config: ServerCommunicationConfig,
106    ) -> Result<(), String> {
107        self.0
108            .run_in_thread(move |repo| async move {
109                let js_value = serde_wasm_bindgen::to_value(&config).map_err(|e| e.to_string())?;
110                repo.save(hostname, js_value)
111                    .await
112                    .map_err(|e| format!("{e:?}"))
113            })
114            .await
115            .map_err(|e| e.to_string())?
116    }
117}