From a80401391cd7a1dc5c5f3e18076c9ee9e4f986b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 17:09:05 +0200 Subject: [PATCH 1/8] [PM-22591] Unify client repository initialization --- Cargo.lock | 11 +- Cargo.toml | 1 - crates/bitwarden-pm/Cargo.toml | 9 +- crates/bitwarden-pm/src/lib.rs | 7 + crates/bitwarden-pm/src/migrations.rs | 32 ++++ crates/bitwarden-state-migrations/Cargo.toml | 21 --- crates/bitwarden-state-migrations/README.md | 4 - crates/bitwarden-state-migrations/src/lib.rs | 15 -- crates/bitwarden-state/README.md | 58 +++---- crates/bitwarden-uniffi/Cargo.toml | 1 - crates/bitwarden-uniffi/src/platform/mod.rs | 26 +++- .../src/platform/repository.rs | 120 +++++++++------ crates/bitwarden-wasm-internal/Cargo.toml | 1 - .../src/platform/mod.rs | 28 +++- .../src/platform/repository.rs | 142 +++++++++++------- 15 files changed, 271 insertions(+), 205 deletions(-) create mode 100644 crates/bitwarden-pm/src/migrations.rs delete mode 100644 crates/bitwarden-state-migrations/Cargo.toml delete mode 100644 crates/bitwarden-state-migrations/README.md delete mode 100644 crates/bitwarden-state-migrations/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d2eb06438..e0dd346b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -718,6 +718,7 @@ dependencies = [ "bitwarden-fido", "bitwarden-generators", "bitwarden-send", + "bitwarden-state", "bitwarden-vault", "tsify", "uniffi", @@ -800,14 +801,6 @@ dependencies = [ "uniffi", ] -[[package]] -name = "bitwarden-state-migrations" -version = "1.0.0" -dependencies = [ - "bitwarden-state", - "bitwarden-vault", -] - [[package]] name = "bitwarden-test" version = "1.0.0" @@ -857,7 +850,6 @@ dependencies = [ "bitwarden-send", "bitwarden-ssh", "bitwarden-state", - "bitwarden-state-migrations", "bitwarden-uniffi-error", "bitwarden-vault", "chrono", @@ -943,7 +935,6 @@ dependencies = [ "bitwarden-pm", "bitwarden-ssh", "bitwarden-state", - "bitwarden-state-migrations", "bitwarden-threading", "bitwarden-vault", "console_error_panic_hook", diff --git a/Cargo.toml b/Cargo.toml index 00d90641d..55d09da09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ bitwarden-send = { path = "crates/bitwarden-send", version = "=1.0.0" } bitwarden-sm = { path = "bitwarden_license/bitwarden-sm", version = "=1.0.0" } bitwarden-ssh = { path = "crates/bitwarden-ssh", version = "=1.0.0" } bitwarden-state = { path = "crates/bitwarden-state", version = "=1.0.0" } -bitwarden-state-migrations = { path = "crates/bitwarden-state-migrations", version = "=1.0.0" } bitwarden-test = { path = "crates/bitwarden-test", version = "=1.0.0" } bitwarden-threading = { path = "crates/bitwarden-threading", version = "=1.0.0" } bitwarden-uniffi-error = { path = "crates/bitwarden-uniffi-error", version = "=1.0.0" } diff --git a/crates/bitwarden-pm/Cargo.toml b/crates/bitwarden-pm/Cargo.toml index 6ce547bdf..abf733635 100644 --- a/crates/bitwarden-pm/Cargo.toml +++ b/crates/bitwarden-pm/Cargo.toml @@ -16,7 +16,7 @@ keywords.workspace = true [features] no-memory-hardening = [ - "bitwarden-core/no-memory-hardening" + "bitwarden-core/no-memory-hardening", ] # Disable memory hardening features uniffi = [ "bitwarden-core/uniffi", @@ -24,18 +24,20 @@ uniffi = [ "bitwarden-fido/uniffi", "bitwarden-generators/uniffi", "bitwarden-send/uniffi", + "bitwarden-state/uniffi", "bitwarden-vault/uniffi", - "dep:uniffi" + "dep:uniffi", ] # Uniffi bindings wasm = [ "bitwarden-auth/wasm", "bitwarden-core/wasm", "bitwarden-exporters/wasm", "bitwarden-generators/wasm", + "bitwarden-state/wasm", "bitwarden-vault/wasm", "dep:wasm-bindgen", "dep:wasm-bindgen-futures", - "dep:tsify" + "dep:tsify", ] # WASM support [dependencies] @@ -45,6 +47,7 @@ bitwarden-exporters = { workspace = true } bitwarden-fido = { workspace = true } bitwarden-generators = { workspace = true } bitwarden-send = { workspace = true } +bitwarden-state = { workspace = true } bitwarden-vault = { workspace = true } tsify = { workspace = true, optional = true } diff --git a/crates/bitwarden-pm/src/lib.rs b/crates/bitwarden-pm/src/lib.rs index acdad2d3f..cdd3f11d8 100644 --- a/crates/bitwarden-pm/src/lib.rs +++ b/crates/bitwarden-pm/src/lib.rs @@ -22,6 +22,8 @@ pub mod clients { pub use bitwarden_vault::VaultClient; } +pub mod migrations; + /// The main entry point for the Bitwarden Password Manager SDK pub struct PasswordManagerClient(pub bitwarden_core::Client); @@ -41,6 +43,11 @@ impl PasswordManagerClient { )) } + /// Platform operations + pub fn platform(&self) -> bitwarden_core::platform::PlatformClient { + self.0.platform() + } + /// Auth operations pub fn auth(&self) -> bitwarden_auth::AuthClient { self.0.auth_new() diff --git a/crates/bitwarden-pm/src/migrations.rs b/crates/bitwarden-pm/src/migrations.rs new file mode 100644 index 000000000..fe28ce08f --- /dev/null +++ b/crates/bitwarden-pm/src/migrations.rs @@ -0,0 +1,32 @@ +//! Manages repository migrations for the Bitwarden SDK. + +use bitwarden_state::repository::{RepositoryItem, RepositoryMigrationStep, RepositoryMigrations}; +use bitwarden_vault::{Cipher, Folder}; + +/// Returns a list of all SDK-managed repository migrations. +pub fn get_sdk_managed_migrations() -> RepositoryMigrations { + use RepositoryMigrationStep::*; + RepositoryMigrations::new(vec![ + // Add any new migrations here. Note that order matters, and that removing a repository + // requires a separate migration step using `Remove(...)`. + Add(Cipher::data()), + Add(Folder::data()), + ]) +} + +/// Macro to create the client managed repositories for the SDK. +/// To add a new repository, add it to the list in the macro invocation. +/// This is meant to be used by the final application crates (e.g., bitwarden-uniffi, +/// bitwarden-wasm-internal, bw). +#[macro_export] +macro_rules! create_client_managed_repositories { + ($container_name:ident, $macro:ident) => { + $macro! { + $container_name; + // List any SDK-managed repositories here. The format is: + // : , + ::bitwarden_vault::Cipher : cipher, "Repository", CipherRepository; + ::bitwarden_vault::Folder : folder, "Repository", FolderRepository; + } + }; +} diff --git a/crates/bitwarden-state-migrations/Cargo.toml b/crates/bitwarden-state-migrations/Cargo.toml deleted file mode 100644 index f0cadc8d0..000000000 --- a/crates/bitwarden-state-migrations/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "bitwarden-state-migrations" -version.workspace = true -authors.workspace = true -edition.workspace = true -rust-version.workspace = true -homepage.workspace = true -repository.workspace = true -license-file.workspace = true -keywords.workspace = true - -[features] -uniffi = ["bitwarden-vault/uniffi"] -wasm = ["bitwarden-vault/wasm"] - -[dependencies] -bitwarden-state = { workspace = true } -bitwarden-vault = { workspace = true } - -[lints] -workspace = true diff --git a/crates/bitwarden-state-migrations/README.md b/crates/bitwarden-state-migrations/README.md deleted file mode 100644 index a0fa7635f..000000000 --- a/crates/bitwarden-state-migrations/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# bitwarden-state-migrations - -This crate contains migrations for the SDK-managed state framework. It should only be imported by -the final application crates (`bitwarden-wasm-internal` and `bitwarden-uniffi`) diff --git a/crates/bitwarden-state-migrations/src/lib.rs b/crates/bitwarden-state-migrations/src/lib.rs deleted file mode 100644 index b6585cac2..000000000 --- a/crates/bitwarden-state-migrations/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![doc = include_str!("../README.md")] - -use bitwarden_state::repository::{RepositoryItem, RepositoryMigrationStep, RepositoryMigrations}; -use bitwarden_vault::{Cipher, Folder}; - -/// Returns a list of all SDK-managed repository migrations. -pub fn get_sdk_managed_migrations() -> RepositoryMigrations { - use RepositoryMigrationStep::*; - RepositoryMigrations::new(vec![ - // Add any new migrations here. Note that order matters, and that removing a repository - // requires a separate migration step using `Remove(...)`. - Add(Cipher::data()), - Add(Folder::data()), - ]) -} diff --git a/crates/bitwarden-state/README.md b/crates/bitwarden-state/README.md index bda45c114..a107cd731 100644 --- a/crates/bitwarden-state/README.md +++ b/crates/bitwarden-state/README.md @@ -39,21 +39,20 @@ need to define some functions in `bitwarden-wasm-internal` and `bitwarden-uniffi applications to provide their `Repository` implementations. The implementations themselves will be very simple as we provide macros that take care of the brunt of the work. -### Client-Managed State in WASM +To add support for a new `Repository`, add it to the list defined at the end of +`crates/bitwarden-pm/src/migrations.rs`. -For WASM, we need to define a new `Repository` for our type and provide a function that will accept -it. This is done in the file `crates/bitwarden-wasm-internal/src/platform/mod.rs`, you can check the -provided example: - -```rust,ignore -repository::create_wasm_repository!(CipherRepository, Cipher, "Repository"); - -#[wasm_bindgen] -impl StateClient { - pub fn register_cipher_repository(&self, store: CipherRepository) { - let store = store.into_channel_impl(); - self.0.platform().state().register_client_managed(store) - } +```rust +macro_rules! create_client_managed_repositories { + ($container_name:ident, $macro:ident) => { + $macro! { + $container_name; + // List any SDK-managed repositories here. The format is: + // : , + ::bitwarden_vault::Cipher : cipher, "Repository", CipherRepository; + ::bitwarden_vault::Folder : folder, "Repository", FolderRepository; + } + }; } ``` @@ -90,33 +89,14 @@ export async function initializeState( stateClient: StateClient, stateProvider: StateProvider, ): Promise { - await stateClient.register_cipher_repository( - new RepositoryRecord(userId, stateProvider, new CipherRecordMapper()), + stateClient.register_client_managed_repositories( + new Repositories( + new RepositoryRecord(userId, stateProvider, new CipherRecordMapper()), + ), ); } ``` -### Client-Managed State in UniFFI - -For UniFFI, we need to define a new `Repository` for our type and provide a function that will -accept it. This is done in the file `crates/bitwarden-uniffi/src/platform/mod.rs`, you can check the -provided example: - -```rust,ignore -repository::create_uniffi_repository!(CipherRepository, Cipher); - -#[uniffi::export] -impl StateClient { - pub fn register_cipher_repository(&self, store: Arc) { - let store_internal = UniffiRepositoryBridge::new(store); - self.0 - .platform() - .state() - .register_client_managed(store_internal) - } -} -``` - #### How to initialize Client-Managed State on iOS Once we have the function defined in `bitwarden-uniffi`, we can use it from the iOS application: @@ -187,8 +167,8 @@ SDK. To add support for an SDK managed `Repository`, a new migration step needs ### How to initialize SDK-Managed State -Go to `crates/bitwarden-state-migrations/src/lib.rs` and add a line with your type, as shown below. -In this example we're registering `Cipher` and `Folder` as SDK managed. +Go to `crates/bitwarden-pm/src/migrations.rs` and add a line with your type, as shown below. In this +example we're registering `Cipher` and `Folder` as SDK managed. ```rust,ignore /// Returns a list of all SDK-managed repository migrations. diff --git a/crates/bitwarden-uniffi/Cargo.toml b/crates/bitwarden-uniffi/Cargo.toml index ab7b6c60a..82aa7a7bb 100644 --- a/crates/bitwarden-uniffi/Cargo.toml +++ b/crates/bitwarden-uniffi/Cargo.toml @@ -31,7 +31,6 @@ bitwarden-pm = { workspace = true, features = ["uniffi"] } bitwarden-send = { workspace = true, features = ["uniffi"] } bitwarden-ssh = { workspace = true, features = ["uniffi"] } bitwarden-state = { workspace = true, features = ["uniffi"] } -bitwarden-state-migrations = { workspace = true, features = ["uniffi"] } bitwarden-uniffi-error = { workspace = true } bitwarden-vault = { workspace = true, features = ["uniffi"] } chrono = { workspace = true, features = ["std"] } diff --git a/crates/bitwarden-uniffi/src/platform/mod.rs b/crates/bitwarden-uniffi/src/platform/mod.rs index 8f71c5775..57dff5143 100644 --- a/crates/bitwarden-uniffi/src/platform/mod.rs +++ b/crates/bitwarden-uniffi/src/platform/mod.rs @@ -1,10 +1,12 @@ +#![allow(deprecated)] + use std::sync::Arc; use bitwarden_core::{Client, platform::FingerprintRequest}; use bitwarden_fido::ClientFido2Ext; use bitwarden_state::DatabaseConfiguration; use bitwarden_vault::Cipher; -use repository::UniffiRepositoryBridge; +use repository::{UniffiRepositoryBridge, create_uniffi_repositories}; use crate::error::Result; @@ -45,24 +47,40 @@ impl PlatformClient { #[derive(uniffi::Object)] pub struct StateClient(Client); -repository::create_uniffi_repository!(CipherRepository, Cipher); - #[derive(uniffi::Record)] pub struct SqliteConfiguration { db_name: String, folder_path: String, } +bitwarden_pm::create_client_managed_repositories!(Repositories, create_uniffi_repositories); + #[uniffi::export] impl StateClient { + #[deprecated(note = "Use `register_client_managed_repositories` instead")] pub fn register_cipher_repository(&self, repository: Arc) { let cipher = UniffiRepositoryBridge::new(repository); self.0.platform().state().register_client_managed(cipher); } + pub fn register_client_managed_repositories(&self, repositories: Repositories) { + repositories.register_all(&self.0.platform().state()); + } + + pub async fn test(&self) -> String { + let s = self + .0 + .platform() + .state() + .get_client_managed::() + .expect("msg"); + + s.list().await.expect("msg").len().to_string() + } + /// Initialize the database for SDK managed repositories. pub async fn initialize_state(&self, configuration: SqliteConfiguration) -> Result<()> { - let migrations = bitwarden_state_migrations::get_sdk_managed_migrations(); + let migrations = bitwarden_pm::migrations::get_sdk_managed_migrations(); self.0 .platform() diff --git a/crates/bitwarden-uniffi/src/platform/repository.rs b/crates/bitwarden-uniffi/src/platform/repository.rs index 7cee81918..b9c1bba35 100644 --- a/crates/bitwarden-uniffi/src/platform/repository.rs +++ b/crates/bitwarden-uniffi/src/platform/repository.rs @@ -39,60 +39,82 @@ impl From for bitwarden_state::repository::RepositoryError { /// This macro creates a Uniffi repository trait and its implementation for the /// [bitwarden_state::repository::Repository] trait -macro_rules! create_uniffi_repository { - ($name:ident, $ty:ty) => { - #[uniffi::export(with_foreign)] - #[async_trait::async_trait] - pub trait $name: Send + Sync { - async fn get( - &self, - id: String, - ) -> Result, $crate::platform::repository::RepositoryError>; - async fn list(&self) - -> Result, $crate::platform::repository::RepositoryError>; - async fn set( - &self, - id: String, - value: $ty, - ) -> Result<(), $crate::platform::repository::RepositoryError>; - async fn remove( - &self, - id: String, - ) -> Result<(), $crate::platform::repository::RepositoryError>; +macro_rules! create_uniffi_repositories { + ( $container_name:ident ; $( $type_name:ty : $field_name:ident, $typescript_ty:literal, $repo_name:ident );+ $(;)? ) => { - async fn has( - &self, - id: String, - ) -> Result; + #[derive(::uniffi::Record)] + pub struct $container_name { + $( + pub $field_name: Option<::std::sync::Arc>, + )+ } - #[async_trait::async_trait] - impl bitwarden_state::repository::Repository<$ty> - for $crate::platform::repository::UniffiRepositoryBridge> - { - async fn get( - &self, - key: String, - ) -> Result, bitwarden_state::repository::RepositoryError> { - self.0.get(key).await.map_err(Into::into) + impl $container_name { + pub fn register_all(self, client: &bitwarden_core::platform::StateClient) { + $( + if let Some(repo) = self.$field_name { + let bridge = $crate::platform::repository::UniffiRepositoryBridge::new(repo); + client.register_client_managed(bridge); + } + )+ } - async fn list(&self) -> Result, bitwarden_state::repository::RepositoryError> { - self.0.list().await.map_err(Into::into) - } - async fn set( - &self, - key: String, - value: $ty, - ) -> Result<(), bitwarden_state::repository::RepositoryError> { - self.0.set(key, value).await.map_err(Into::into) + } + + $( + #[::uniffi::export(with_foreign)] + #[::async_trait::async_trait] + pub trait $repo_name: Send + Sync { + async fn get( + &self, + id: String, + ) -> Result, $crate::platform::repository::RepositoryError>; + async fn list(&self) + -> Result, $crate::platform::repository::RepositoryError>; + async fn set( + &self, + id: String, + value: $type_name, + ) -> Result<(), $crate::platform::repository::RepositoryError>; + async fn remove( + &self, + id: String, + ) -> Result<(), $crate::platform::repository::RepositoryError>; + + async fn has( + &self, + id: String, + ) -> Result; } - async fn remove( - &self, - key: String, - ) -> Result<(), bitwarden_state::repository::RepositoryError> { - self.0.remove(key).await.map_err(Into::into) + + #[async_trait::async_trait] + impl bitwarden_state::repository::Repository<$type_name> + for $crate::platform::repository::UniffiRepositoryBridge> + { + async fn get( + &self, + key: String, + ) -> Result, bitwarden_state::repository::RepositoryError> { + self.0.get(key).await.map_err(Into::into) + } + async fn list(&self) -> Result, bitwarden_state::repository::RepositoryError> { + self.0.list().await.map_err(Into::into) + } + async fn set( + &self, + key: String, + value: $type_name, + ) -> Result<(), bitwarden_state::repository::RepositoryError> { + self.0.set(key, value).await.map_err(Into::into) + } + async fn remove( + &self, + key: String, + ) -> Result<(), bitwarden_state::repository::RepositoryError> { + self.0.remove(key).await.map_err(Into::into) + } } - } + )+ }; } -pub(super) use create_uniffi_repository; + +pub(super) use create_uniffi_repositories; diff --git a/crates/bitwarden-wasm-internal/Cargo.toml b/crates/bitwarden-wasm-internal/Cargo.toml index d582d688a..7012ee07f 100644 --- a/crates/bitwarden-wasm-internal/Cargo.toml +++ b/crates/bitwarden-wasm-internal/Cargo.toml @@ -24,7 +24,6 @@ bitwarden-ipc = { workspace = true, features = ["wasm"] } bitwarden-pm = { workspace = true, features = ["wasm"] } bitwarden-ssh = { workspace = true, features = ["wasm"] } bitwarden-state = { workspace = true, features = ["wasm"] } -bitwarden-state-migrations = { workspace = true, features = ["wasm"] } bitwarden-threading = { workspace = true } bitwarden-vault = { workspace = true, features = ["wasm"] } console_error_panic_hook = "0.1.7" diff --git a/crates/bitwarden-wasm-internal/src/platform/mod.rs b/crates/bitwarden-wasm-internal/src/platform/mod.rs index daf85ac65..b5906618a 100644 --- a/crates/bitwarden-wasm-internal/src/platform/mod.rs +++ b/crates/bitwarden-wasm-internal/src/platform/mod.rs @@ -1,10 +1,12 @@ use bitwarden_core::Client; use bitwarden_state::DatabaseConfiguration; -use bitwarden_vault::{Cipher, Folder}; +use bitwarden_vault::Cipher; use serde::{Deserialize, Serialize}; use tsify::Tsify; use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; +use crate::platform::repository::create_wasm_repositories; + mod repository; pub mod token_provider; @@ -48,8 +50,7 @@ impl StateClient { } } -repository::create_wasm_repository!(CipherRepository, Cipher, "Repository"); -repository::create_wasm_repository!(FolderRepository, Folder, "Repository"); +bitwarden_pm::create_client_managed_repositories!(Repositories, create_wasm_repositories); #[derive(Serialize, Deserialize, Tsify)] #[tsify(into_wasm_abi, from_wasm_abi)] @@ -59,22 +60,41 @@ pub struct IndexedDbConfiguration { #[wasm_bindgen] impl StateClient { + #[allow(deprecated)] + #[deprecated(note = "Use `register_client_managed_repositories` instead")] pub fn register_cipher_repository(&self, cipher_repository: CipherRepository) { let cipher = cipher_repository.into_channel_impl(); self.0.platform().state().register_client_managed(cipher); } + #[allow(deprecated)] + #[deprecated(note = "Use `register_client_managed_repositories` instead")] pub fn register_folder_repository(&self, store: FolderRepository) { let store = store.into_channel_impl(); self.0.platform().state().register_client_managed(store) } + pub fn register_client_managed_repositories(&self, repositories: Repositories) { + repositories.register_all(&self.0.platform().state()); + } + + pub async fn test(&self) -> String { + let s = self + .0 + .platform() + .state() + .get_client_managed::() + .expect("msg"); + + s.list().await.expect("msg").len().to_string() + } + /// Initialize the database for SDK managed repositories. pub async fn initialize_state( &self, configuration: IndexedDbConfiguration, ) -> Result<(), bitwarden_state::registry::StateRegistryError> { - let sdk_managed_repositories = bitwarden_state_migrations::get_sdk_managed_migrations(); + let sdk_managed_repositories = bitwarden_pm::migrations::get_sdk_managed_migrations(); self.0 .platform() diff --git a/crates/bitwarden-wasm-internal/src/platform/repository.rs b/crates/bitwarden-wasm-internal/src/platform/repository.rs index 3ec4033f5..0d85eb98e 100644 --- a/crates/bitwarden-wasm-internal/src/platform/repository.rs +++ b/crates/bitwarden-wasm-internal/src/platform/repository.rs @@ -95,70 +95,106 @@ export interface Repository { /// This macro generates a [::wasm_bindgen] interface for a repository type, and provides the /// implementation of [WasmRepository] and a way to convert it into something that implements /// the [Repository] trait. -macro_rules! create_wasm_repository { - ($name:ident, $ty:ty, $typescript_ty:literal) => { +macro_rules! create_wasm_repositories { + ( $container_name:ident ; $( $type_name:ty : $field_name:ident, $typescript_ty:literal, $repo_name:ident );+ $(;)? ) => { + + const _: () = { + #[wasm_bindgen(typescript_custom_section)] + const REPOSITORIES_CUSTOM_TS_TYPE: &'static str = concat!( + "export interface ", + stringify!($container_name), + " {\n", + $( " ", stringify!($field_name), ": ", $typescript_ty, " | null;\n", )+ + "}\n" + ); + }; + #[wasm_bindgen] extern "C" { - #[wasm_bindgen(js_name = $name, typescript_type = $typescript_ty)] - pub type $name; - - #[wasm_bindgen(method, catch)] - async fn get( - this: &$name, - id: String, - ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; - #[wasm_bindgen(method, catch)] - async fn list(this: &$name) - -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; - #[wasm_bindgen(method, catch)] - async fn set( - this: &$name, - id: String, - value: $ty, - ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; - #[wasm_bindgen(method, catch)] - async fn remove( - this: &$name, - id: String, - ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; + #[wasm_bindgen(typescript_type = $container_name)] + pub type $container_name; + + $( + #[wasm_bindgen(method, getter)] + pub fn $field_name(this: &$container_name) -> Option<$repo_name>; + )+ } - impl $crate::platform::repository::WasmRepository<$ty> for $name { - async fn get( - &self, - id: String, - ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { - self.get(id).await + impl $container_name { + pub fn register_all(self, client: &bitwarden_core::platform::StateClient) { + $( + if let Some(repo) = self.$field_name() { + let repo = repo.into_channel_impl(); + client.register_client_managed(repo); + } + )+ } - async fn list(&self) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { - self.list().await - } - async fn set( - &self, - id: String, - value: $ty, - ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { - self.set(id, value).await + } + + $( + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(js_name = $repo_name, typescript_type = $typescript_ty)] + pub type $repo_name; + + #[wasm_bindgen(method, catch)] + async fn get( + this: &$repo_name, + id: String, + ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; + #[wasm_bindgen(method, catch)] + async fn list(this: &$repo_name) + -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; + #[wasm_bindgen(method, catch)] + async fn set( + this: &$repo_name, + id: String, + value: $type_name, + ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; + #[wasm_bindgen(method, catch)] + async fn remove( + this: &$repo_name, + id: String, + ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; } - async fn remove( - &self, - id: String, - ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { - self.remove(id).await + + impl $crate::platform::repository::WasmRepository<$type_name> for $repo_name { + async fn get( + &self, + id: String, + ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { + self.get(id).await + } + async fn list(&self) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { + self.list().await + } + async fn set( + &self, + id: String, + value: $type_name, + ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { + self.set(id, value).await + } + async fn remove( + &self, + id: String, + ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { + self.remove(id).await + } } - } - impl $name { - pub fn into_channel_impl( - self, - ) -> ::std::sync::Arc> { - use $crate::platform::repository::WasmRepositoryChannel; - ::std::sync::Arc::new(WasmRepositoryChannel::new(self)) + impl $repo_name { + pub fn into_channel_impl( + self, + ) -> ::std::sync::Arc> { + use $crate::platform::repository::WasmRepositoryChannel; + ::std::sync::Arc::new(WasmRepositoryChannel::new(self)) + } } - } + )+ }; } -pub(crate) use create_wasm_repository; +pub(crate) use create_wasm_repositories; const UNIT: Result = Ok(JsValue::UNDEFINED); From 73794c9aa7d8ee18a3b3c92ee9c01967096769d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 17:14:31 +0200 Subject: [PATCH 2/8] Sort --- crates/bitwarden-pm/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-pm/Cargo.toml b/crates/bitwarden-pm/Cargo.toml index abf733635..f0c836bce 100644 --- a/crates/bitwarden-pm/Cargo.toml +++ b/crates/bitwarden-pm/Cargo.toml @@ -16,7 +16,7 @@ keywords.workspace = true [features] no-memory-hardening = [ - "bitwarden-core/no-memory-hardening", + "bitwarden-core/no-memory-hardening" ] # Disable memory hardening features uniffi = [ "bitwarden-core/uniffi", @@ -26,7 +26,7 @@ uniffi = [ "bitwarden-send/uniffi", "bitwarden-state/uniffi", "bitwarden-vault/uniffi", - "dep:uniffi", + "dep:uniffi" ] # Uniffi bindings wasm = [ "bitwarden-auth/wasm", @@ -37,7 +37,7 @@ wasm = [ "bitwarden-vault/wasm", "dep:wasm-bindgen", "dep:wasm-bindgen-futures", - "dep:tsify", + "dep:tsify" ] # WASM support [dependencies] From d7bc50b7a9e9e9d93d02159179a4ebcc4c4a2293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 17:33:57 +0200 Subject: [PATCH 3/8] Fmt --- crates/bitwarden-state/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bitwarden-state/README.md b/crates/bitwarden-state/README.md index a107cd731..573f55ce7 100644 --- a/crates/bitwarden-state/README.md +++ b/crates/bitwarden-state/README.md @@ -90,9 +90,7 @@ export async function initializeState( stateProvider: StateProvider, ): Promise { stateClient.register_client_managed_repositories( - new Repositories( - new RepositoryRecord(userId, stateProvider, new CipherRecordMapper()), - ), + new Repositories(new RepositoryRecord(userId, stateProvider, new CipherRecordMapper())), ); } ``` From d8fa5794f8148cb26a0edea818ae26e902b53869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 17:50:51 +0200 Subject: [PATCH 4/8] Remove test --- crates/bitwarden-uniffi/src/platform/mod.rs | 12 ------------ crates/bitwarden-wasm-internal/src/platform/mod.rs | 12 ------------ 2 files changed, 24 deletions(-) diff --git a/crates/bitwarden-uniffi/src/platform/mod.rs b/crates/bitwarden-uniffi/src/platform/mod.rs index 57dff5143..8dcdaeb4e 100644 --- a/crates/bitwarden-uniffi/src/platform/mod.rs +++ b/crates/bitwarden-uniffi/src/platform/mod.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use bitwarden_core::{Client, platform::FingerprintRequest}; use bitwarden_fido::ClientFido2Ext; use bitwarden_state::DatabaseConfiguration; -use bitwarden_vault::Cipher; use repository::{UniffiRepositoryBridge, create_uniffi_repositories}; use crate::error::Result; @@ -67,17 +66,6 @@ impl StateClient { repositories.register_all(&self.0.platform().state()); } - pub async fn test(&self) -> String { - let s = self - .0 - .platform() - .state() - .get_client_managed::() - .expect("msg"); - - s.list().await.expect("msg").len().to_string() - } - /// Initialize the database for SDK managed repositories. pub async fn initialize_state(&self, configuration: SqliteConfiguration) -> Result<()> { let migrations = bitwarden_pm::migrations::get_sdk_managed_migrations(); diff --git a/crates/bitwarden-wasm-internal/src/platform/mod.rs b/crates/bitwarden-wasm-internal/src/platform/mod.rs index b5906618a..6953557af 100644 --- a/crates/bitwarden-wasm-internal/src/platform/mod.rs +++ b/crates/bitwarden-wasm-internal/src/platform/mod.rs @@ -1,6 +1,5 @@ use bitwarden_core::Client; use bitwarden_state::DatabaseConfiguration; -use bitwarden_vault::Cipher; use serde::{Deserialize, Serialize}; use tsify::Tsify; use wasm_bindgen::{JsValue, prelude::wasm_bindgen}; @@ -78,17 +77,6 @@ impl StateClient { repositories.register_all(&self.0.platform().state()); } - pub async fn test(&self) -> String { - let s = self - .0 - .platform() - .state() - .get_client_managed::() - .expect("msg"); - - s.list().await.expect("msg").len().to_string() - } - /// Initialize the database for SDK managed repositories. pub async fn initialize_state( &self, From 81dca37552800a745ba5207a3617b4114ba8132d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 17:51:47 +0200 Subject: [PATCH 5/8] Fix doclink --- crates/bitwarden-wasm-internal/src/platform/repository.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-wasm-internal/src/platform/repository.rs b/crates/bitwarden-wasm-internal/src/platform/repository.rs index 0d85eb98e..fcf50af9e 100644 --- a/crates/bitwarden-wasm-internal/src/platform/repository.rs +++ b/crates/bitwarden-wasm-internal/src/platform/repository.rs @@ -29,7 +29,7 @@ * use the existing [Repository] trait. * - It runs the calls in a thread-bound manner, so we can safely call the [WasmRepository] * methods from any thread. - * - The [create_wasm_repository] macro, defines the [::wasm_bindgen] interface and implements + * - The [create_wasm_repositories] macro, defines the [::wasm_bindgen] interface and implements * the [WasmRepository] trait for you. */ From 1223623e1364d5c042d74b389637a3404f01be0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 20:59:08 +0200 Subject: [PATCH 6/8] Improve definition --- crates/bitwarden-pm/src/migrations.rs | 6 +++--- crates/bitwarden-state/README.md | 6 +++--- .../bitwarden-uniffi/src/platform/repository.rs | 16 ++++++++-------- .../src/platform/repository.rs | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/bitwarden-pm/src/migrations.rs b/crates/bitwarden-pm/src/migrations.rs index fe28ce08f..242ae87e3 100644 --- a/crates/bitwarden-pm/src/migrations.rs +++ b/crates/bitwarden-pm/src/migrations.rs @@ -24,9 +24,9 @@ macro_rules! create_client_managed_repositories { $macro! { $container_name; // List any SDK-managed repositories here. The format is: - // : , - ::bitwarden_vault::Cipher : cipher, "Repository", CipherRepository; - ::bitwarden_vault::Folder : folder, "Repository", FolderRepository; + // , , , + ::bitwarden_vault::Cipher, Cipher, cipher, CipherRepository; + ::bitwarden_vault::Folder, Folder, folder, FolderRepository; } }; } diff --git a/crates/bitwarden-state/README.md b/crates/bitwarden-state/README.md index 573f55ce7..033599117 100644 --- a/crates/bitwarden-state/README.md +++ b/crates/bitwarden-state/README.md @@ -48,9 +48,9 @@ macro_rules! create_client_managed_repositories { $macro! { $container_name; // List any SDK-managed repositories here. The format is: - // : , - ::bitwarden_vault::Cipher : cipher, "Repository", CipherRepository; - ::bitwarden_vault::Folder : folder, "Repository", FolderRepository; + // , , , + ::bitwarden_vault::Cipher, Cipher, cipher, CipherRepository; + ::bitwarden_vault::Folder, Folder, folder, FolderRepository; } }; } diff --git a/crates/bitwarden-uniffi/src/platform/repository.rs b/crates/bitwarden-uniffi/src/platform/repository.rs index b9c1bba35..973afd3a0 100644 --- a/crates/bitwarden-uniffi/src/platform/repository.rs +++ b/crates/bitwarden-uniffi/src/platform/repository.rs @@ -40,7 +40,7 @@ impl From for bitwarden_state::repository::RepositoryError { /// This macro creates a Uniffi repository trait and its implementation for the /// [bitwarden_state::repository::Repository] trait macro_rules! create_uniffi_repositories { - ( $container_name:ident ; $( $type_name:ty : $field_name:ident, $typescript_ty:literal, $repo_name:ident );+ $(;)? ) => { + ( $container_name:ident ; $( $qualified_type_name:ty, $type_name:ident, $field_name:ident, $repo_name:ident );+ $(;)? ) => { #[derive(::uniffi::Record)] pub struct $container_name { @@ -67,13 +67,13 @@ macro_rules! create_uniffi_repositories { async fn get( &self, id: String, - ) -> Result, $crate::platform::repository::RepositoryError>; + ) -> Result, $crate::platform::repository::RepositoryError>; async fn list(&self) - -> Result, $crate::platform::repository::RepositoryError>; + -> Result, $crate::platform::repository::RepositoryError>; async fn set( &self, id: String, - value: $type_name, + value: $qualified_type_name, ) -> Result<(), $crate::platform::repository::RepositoryError>; async fn remove( &self, @@ -87,22 +87,22 @@ macro_rules! create_uniffi_repositories { } #[async_trait::async_trait] - impl bitwarden_state::repository::Repository<$type_name> + impl bitwarden_state::repository::Repository<$qualified_type_name> for $crate::platform::repository::UniffiRepositoryBridge> { async fn get( &self, key: String, - ) -> Result, bitwarden_state::repository::RepositoryError> { + ) -> Result, bitwarden_state::repository::RepositoryError> { self.0.get(key).await.map_err(Into::into) } - async fn list(&self) -> Result, bitwarden_state::repository::RepositoryError> { + async fn list(&self) -> Result, bitwarden_state::repository::RepositoryError> { self.0.list().await.map_err(Into::into) } async fn set( &self, key: String, - value: $type_name, + value: $qualified_type_name, ) -> Result<(), bitwarden_state::repository::RepositoryError> { self.0.set(key, value).await.map_err(Into::into) } diff --git a/crates/bitwarden-wasm-internal/src/platform/repository.rs b/crates/bitwarden-wasm-internal/src/platform/repository.rs index fcf50af9e..a9fca6be6 100644 --- a/crates/bitwarden-wasm-internal/src/platform/repository.rs +++ b/crates/bitwarden-wasm-internal/src/platform/repository.rs @@ -96,7 +96,7 @@ export interface Repository { /// implementation of [WasmRepository] and a way to convert it into something that implements /// the [Repository] trait. macro_rules! create_wasm_repositories { - ( $container_name:ident ; $( $type_name:ty : $field_name:ident, $typescript_ty:literal, $repo_name:ident );+ $(;)? ) => { + ( $container_name:ident ; $( $qualified_type_name:ty, $type_name:ident, $field_name:ident, $repo_name:ident );+ $(;)? ) => { const _: () = { #[wasm_bindgen(typescript_custom_section)] @@ -134,7 +134,7 @@ macro_rules! create_wasm_repositories { $( #[wasm_bindgen] extern "C" { - #[wasm_bindgen(js_name = $repo_name, typescript_type = $typescript_ty)] + #[wasm_bindgen] pub type $repo_name; #[wasm_bindgen(method, catch)] @@ -149,7 +149,7 @@ macro_rules! create_wasm_repositories { async fn set( this: &$repo_name, id: String, - value: $type_name, + value: $qualified_type_name, ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; #[wasm_bindgen(method, catch)] async fn remove( @@ -158,7 +158,7 @@ macro_rules! create_wasm_repositories { ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue>; } - impl $crate::platform::repository::WasmRepository<$type_name> for $repo_name { + impl $crate::platform::repository::WasmRepository<$qualified_type_name> for $repo_name { async fn get( &self, id: String, @@ -171,7 +171,7 @@ macro_rules! create_wasm_repositories { async fn set( &self, id: String, - value: $type_name, + value: $qualified_type_name, ) -> Result<::wasm_bindgen::JsValue, ::wasm_bindgen::JsValue> { self.set(id, value).await } @@ -186,7 +186,7 @@ macro_rules! create_wasm_repositories { impl $repo_name { pub fn into_channel_impl( self, - ) -> ::std::sync::Arc> { + ) -> ::std::sync::Arc> { use $crate::platform::repository::WasmRepositoryChannel; ::std::sync::Arc::new(WasmRepositoryChannel::new(self)) } From 423282d89f2e075f569b9b52323c65bea35b401a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Wed, 1 Oct 2025 21:01:14 +0200 Subject: [PATCH 7/8] Missed rename --- crates/bitwarden-wasm-internal/src/platform/repository.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-wasm-internal/src/platform/repository.rs b/crates/bitwarden-wasm-internal/src/platform/repository.rs index a9fca6be6..faa4db8a0 100644 --- a/crates/bitwarden-wasm-internal/src/platform/repository.rs +++ b/crates/bitwarden-wasm-internal/src/platform/repository.rs @@ -103,8 +103,8 @@ macro_rules! create_wasm_repositories { const REPOSITORIES_CUSTOM_TS_TYPE: &'static str = concat!( "export interface ", stringify!($container_name), - " {\n", - $( " ", stringify!($field_name), ": ", $typescript_ty, " | null;\n", )+ + "{\n", + $( stringify!($field_name), ": Repository<", stringify!($type_name), "> | null;\n", )+ "}\n" ); }; From dd210413e855b2eba7463cfb41aa8d14a9e9aaa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Thu, 2 Oct 2025 15:41:45 +0200 Subject: [PATCH 8/8] Update doc --- crates/bitwarden-state/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-state/README.md b/crates/bitwarden-state/README.md index 033599117..f3f72dfca 100644 --- a/crates/bitwarden-state/README.md +++ b/crates/bitwarden-state/README.md @@ -89,9 +89,9 @@ export async function initializeState( stateClient: StateClient, stateProvider: StateProvider, ): Promise { - stateClient.register_client_managed_repositories( - new Repositories(new RepositoryRecord(userId, stateProvider, new CipherRecordMapper())), - ); + stateClient.register_client_managed_repositories({ + cipher: new RepositoryRecord(userId, stateProvider, new CipherRecordMapper()), + }); } ```