# DOMException → WebAuthnError mapper ## Purpose Converts every `DOMException.name` raised by `navigator.credentials.create` and `navigator.credentials.get` into a typed `WebAuthnError`. Raw `DOMException` instances never reach the caller — the mapper is invoked at every adapter boundary so consumers can rely on `instanceof WebAuthnError` and `switch (err.code)` instead of pattern-matching on browser-side string names. The original `DOMException` is preserved on `err.cause` for forensic inspection. ## Features - Five fully-specified mappings per the W3C WebAuthn L3 §5.1.3 / §5.1.4 informative error tables: - `NotAllowedError` → `WEBAUTHN.USER_CANCELLED` - `InvalidStateError` → `WEBAUTHN.INVALID_STATE` - `SecurityError` → `WEBAUTHN.SECURITY_ERROR` - `AbortError` → `WEBAUTHN.ABORTED` - `NotSupportedError` → `WEBAUTHN.NOT_SUPPORTED` - One additional mapping for `TimeoutError` → `WEBAUTHN.TIMEOUT`, which some browsers raise instead of `AbortError` on the `timeout` field. - Unknown error names map to `WEBAUTHN.MALFORMED_RESPONSE` with `cause` set so callers can still drill into the original error. - Pass-through for an already-typed `WebAuthnError`: the mapper is idempotent so registration / authentication wrappers can wrap it twice without losing the original `code`. ## Usage: Basic Example ```ts import { mapDomException } from "@suaveplan/webauthn/adapter"; try { await navigator.credentials.create({ publicKey }); } catch (err) { throw mapDomException(err); } ``` ## Usage: Advanced Example ```ts import { mapDomException, WebAuthnError } from "@suaveplan/webauthn"; async function safeCreate(opts: CredentialCreationOptions) { try { return await navigator.credentials.create(opts); } catch (err) { const mapped = mapDomException(err); if (mapped.code === "WEBAUTHN.USER_CANCELLED") { // Tell the user politely, do not retry automatically. } throw mapped; } } ``` ## API Reference - `mapDomException(err: unknown): WebAuthnError` — returns a `WebAuthnError`; preserves `cause`. ## Implementation Notes - The mapper reads `err.name` defensively so any value that ducktypes as `{ name: string }` is recognised. This is necessary because browsers occasionally throw `Error` subclasses that are not `DOMException` instances (older Safari). - `WebAuthnError` instances are returned unchanged — the mapper is idempotent so wrappers can compose without double-wrapping. - The fallback `WEBAUTHN.MALFORMED_RESPONSE` is intentionally distinct from `NOT_SUPPORTED` so the OTel `errors_total{code}` counter highlights authenticator misbehaviour separately from feature detection.