From 1f764a4639c322b07ebc7295b6c58073e5cb266b Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Wed, 29 Apr 2026 23:25:25 -0400 Subject: [PATCH] feat(wasm): parse_lastpass_csv_json bridge Returns { items: [Item], warnings: [ImportWarning] } as a JSON string. The items already have fresh IDs + timestamps; the SW caller encrypts and writes them through the existing item_encrypt + manifest_encrypt bridges. Co-Authored-By: Claude Opus 4.7 --- crates/relicario-wasm/src/lib.rs | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/crates/relicario-wasm/src/lib.rs b/crates/relicario-wasm/src/lib.rs index 4657e58..c57c375 100644 --- a/crates/relicario-wasm/src/lib.rs +++ b/crates/relicario-wasm/src/lib.rs @@ -432,6 +432,28 @@ pub fn unpack_backup_json(bytes: &[u8], passphrase: &str) -> Result Result { + let (items, warnings) = core_parse_lastpass_csv(csv_bytes) + .map_err(|e| JsError::new(&e.to_string()))?; + + let json = serde_json::json!({ + "items": items, + "warnings": warnings, + }); + Ok(json.to_string()) +} + #[cfg(test)] mod session_tests { use super::*; @@ -474,4 +496,31 @@ mod session_tests { let bytes2 = manifest_encrypt(&handle, &serde_json::to_string(&empty).unwrap()).unwrap(); assert_ne!(bytes, bytes2, "nonces must differ"); } + + #[test] + fn parse_lastpass_csv_json_returns_items_and_warnings() { + // Row 1 imports cleanly; row 2 has an empty `name` and is skipped + // with a warning. + let csv = "url,username,password,totp,extra,name,grouping,fav\n\ + https://x,alice,hunter2,,,GitHub,Work,1\n\ + https://y,bob,hunter2,,,,,"; + let json = super::parse_lastpass_csv_json(csv.as_bytes()).unwrap(); + let v: serde_json::Value = serde_json::from_str(&json).unwrap(); + assert_eq!(v["items"].as_array().unwrap().len(), 1); + assert_eq!(v["warnings"].as_array().unwrap().len(), 1); + assert!(v["warnings"][0]["message"].as_str().unwrap().contains("name")); + // The item's title round-trips as a plain JSON string. + assert_eq!(v["items"][0]["title"].as_str().unwrap(), "GitHub"); + } + + #[test] + fn parse_lastpass_csv_json_propagates_header_errors() { + // Test the underlying core function directly since native tests + // can't call wasm_bindgen functions. + use relicario_core::import_lastpass::parse_lastpass_csv; + let bad = "name,user,pass\nA,u,p\n"; + let err = parse_lastpass_csv(bad.as_bytes()); + // Should fail with a header validation error. + assert!(err.is_err()); + } }