feat(wasm): add parse_month_year, base32_decode_lenient, guess_mime exports
Plan B Phase 8 — three #[wasm_bindgen] exports for the parsers migrated
in Phase 7, mirrored in extension/src/wasm.d.ts under "Pure parsers
(no session needed)". snake_case JS naming consistent with every
existing export; SessionHandle not required.
- parse_month_year(s) → { month, year } via js_value_for
- base32_decode_lenient(s) → Uint8Array
- guess_mime(filename) → string
Tests in session_tests mod cover the OK paths; error-path / JsValue
serialization can't be tested natively (JsError construction panics
off-wasm) and is covered in core (time::tests + base32::tests).
Plan C will wire SW message handlers consuming these exports in a
future round; this commit delivers only the seam.
Includes simplify-feedback fixes:
- relicario-core lib.rs module-list mentions base32 and mime
- item_types/totp.rs neighbour comment unified to ///-style block
cargo test --workspace: green
cargo clippy --workspace: silent
cargo build -p relicario-wasm --target wasm32-unknown-unknown: clean
cd extension && npm run test: 17 pre-existing failures only (baseline)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -330,6 +330,32 @@ pub fn embed_image_secret(carrier: &[u8], secret: &[u8]) -> Result<Vec<u8>, JsEr
|
||||
imgsecret::embed(carrier, s).map_err(|e| JsError::new(&e.to_string()))
|
||||
}
|
||||
|
||||
// ── Pure parsers (no session needed) ────────────────────────────────────────
|
||||
|
||||
use relicario_core::{base32 as core_base32, mime as core_mime, MonthYear};
|
||||
|
||||
/// Parse a card-expiry string (`MM/YYYY` / `MM-YYYY` / `MM/YY`).
|
||||
/// Returns a plain `{ month, year }` object on success.
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_month_year(s: &str) -> Result<JsValue, JsError> {
|
||||
let my = MonthYear::parse(s).map_err(|e| JsError::new(&e.to_string()))?;
|
||||
js_value_for(&my)
|
||||
}
|
||||
|
||||
/// Decode an RFC 4648 base32 string (case-insensitive, optional padding,
|
||||
/// whitespace-stripped). Returned as `Uint8Array` on the JS side.
|
||||
#[wasm_bindgen]
|
||||
pub fn base32_decode_lenient(s: &str) -> Result<Vec<u8>, JsError> {
|
||||
core_base32::decode_rfc4648_lenient(s).map_err(|e| JsError::new(&e.to_string()))
|
||||
}
|
||||
|
||||
/// Guess a MIME type from a filename's extension. Returns
|
||||
/// `application/octet-stream` for unknown or missing extensions.
|
||||
#[wasm_bindgen]
|
||||
pub fn guess_mime(filename: &str) -> String {
|
||||
core_mime::guess_for_extension(filename).to_string()
|
||||
}
|
||||
|
||||
use relicario_core::item_types::{TotpConfig, compute_totp_code};
|
||||
|
||||
#[wasm_bindgen]
|
||||
@@ -624,4 +650,24 @@ mod session_tests {
|
||||
// Should fail with a header validation error.
|
||||
assert!(err.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn base32_decode_lenient_round_trips_known_vector() {
|
||||
let bytes = super::base32_decode_lenient("MZXW6YTBOI").unwrap();
|
||||
assert_eq!(bytes, b"foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guess_mime_known_and_unknown_extensions() {
|
||||
assert_eq!(super::guess_mime("doc.pdf"), "application/pdf");
|
||||
assert_eq!(super::guess_mime("photo.JPEG"), "image/jpeg");
|
||||
assert_eq!(super::guess_mime("file.xyz"), "application/octet-stream");
|
||||
}
|
||||
|
||||
// Error paths and JsValue serialization can't be exercised natively —
|
||||
// JsError::new and serde_wasm_bindgen::Serializer call wasm-bindgen
|
||||
// imports that panic off-wasm (same constraint as
|
||||
// `parse_lastpass_csv_json_propagates_header_errors` above). Those
|
||||
// paths are covered in core: `time::tests::parse_rejects_malformed`
|
||||
// and `base32::tests::decode_rfc4648_lenient_rejects_non_alphabet_chars`.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user