feat(dashnote): preview identity + key-type fitness on WIF paste#75
feat(dashnote): preview identity + key-type fitness on WIF paste#75
Conversation
…t shape Login form now accepts either a BIP39 mnemonic or a WIF private key in a single field, dispatching by whitespace presence. WIF flow looks up the identity via byPublicKeyHash, validates the matched key is an AUTHENTICATION key at HIGH or CRITICAL level (rejecting MASTER which can't sign documents), and rejects disabled keys. Failed logins no longer clobber prior session state — they restore the prior status, or fall back to browsing if a remembered identity exists. Co-Authored-By: Claude Opus 4.7 (1M context)
Eagerly resolve the identity (and DPNS name) for a pasted WIF once it passes a cheap structural gate (length 51/52, base58 charset) plus a 400ms debounce. Surface "wrong key type" / "key disabled" warnings pre-submit so users see the issue before clicking Login. Splits a signer-free `resolveIdentityFromWif` out of `loginWithPrivateKey` for shared use; wrong-purpose errors now also carry the security-level name so MASTER auth keys read distinctly from purpose mismatches. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
📝 WalkthroughWalkthroughAdds secret-based login supporting mnemonic or WIF private keys: input-shape detection, lightweight WIF gating, a WIF-to-identity resolution + one-key signer path, a debounced WIF preview hook, session wiring for private-key auth, UI adjustments in the login modal, and comprehensive tests for these flows. ChangesWIF and Private Key Login
Sequence DiagramsequenceDiagram
participant User as User
participant LoginModal as LoginModal
participant Preview as useWifPreview
participant Session as SessionContext
participant LoginPK as loginWithPrivateKey
participant SDK as DashSdk
participant DPNS as DPNS
User->>LoginModal: Enter secret (mnemonic or WIF)
LoginModal->>Preview: detectSecretShape(secret) & preview(secret)
Preview-->>LoginModal: preview state (idle/checking/resolved/error)
User->>LoginModal: Submit secret
LoginModal->>Session: submit secret
Session->>Session: detectSecretShape(secret)
alt mnemonic
Session->>Session: IdentityKeyManager.create(mnemonic)
Session-->>LoginModal: success (keyManager)
else wif
Session->>LoginPK: loginWithPrivateKey(wif)
LoginPK->>SDK: identities.byPublicKeyHash(pubKeyHash)
SDK-->>LoginPK: identity (or null)
LoginPK->>LoginPK: validate purpose/security, build auth
LoginPK-->>Session: auth + identityId
end
Session->>DPNS: resolve name (optional)
DPNS-->>Session: name
Session-->>LoginModal: success / failure
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…equirement Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
example-apps/dashnote/src/session/SessionContext.tsx (1)
74-74: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
SessionValue.loginparameter still typed asmnemonic.The implementation now accepts
secret: string(Line 147) and dispatches viadetectSecretShape, but the public type on the context still readslogin: (mnemonic: string, options?: LoginOptions) => Promise<void>. Rename for accuracy so callers / docs reflect that this accepts either a mnemonic or a WIF.♻️ Proposed fix
- login: (mnemonic: string, options?: LoginOptions) => Promise<void>; + login: (secret: string, options?: LoginOptions) => Promise<void>;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/src/session/SessionContext.tsx` at line 74, The public SessionValue.login type is incorrect: it names the parameter "mnemonic" but the implementation accepts a generic secret and calls detectSecretShape; update the context signature for SessionValue.login to use secret instead of mnemonic (i.e. login: (secret: string, options?: LoginOptions) => Promise<void>) so callers and docs match the implementation that dispatches via detectSecretShape; make the same parameter rename in any exported types/interfaces that reference SessionValue.login to keep types consistent.
🧹 Nitpick comments (5)
example-apps/dashnote/src/dash/loginWithPrivateKey.ts (2)
84-117: 💤 Low valueDoc says "base64 first, then hex" but the code tries hex first.
The block comment claims base64 is tried first as the preferred path, but
tryDecodeKeyDataactually checks the hex regex first (Lines 95-105) and only falls through to base64 (Lines 107-117). Functionally this is fine because the hex regex is strict enough that base64-encoded keys with+///=won't collide, but the comment misleads readers about intent. Either swap the order to match the comment, or update the comment to reflect "hex first because the regex is more discriminating".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/src/dash/loginWithPrivateKey.ts` around lines 84 - 117, The function tryDecodeKeyData currently attempts hex decoding first then falls back to base64, but the block comment claims base64 is tried first; fix by either swapping the decode order in tryDecodeKeyData so base64 is attempted before the hex-regex branch, or update the block comment to state that hex is checked first (because of the strict hex-regex) and base64 is the fallback—ensure the comment and the implementation for tryDecodeKeyData remain consistent.
291-304: 💤 Low valueHex fallback doesn't guard against odd-length output.
tryDecodeKeyDatacorrectly checksdata.length % 2 === 0(Line 95) before parsing as hex, but the equivalent path here (Line 294) only validates the alphabet. IfpubKey.toString("hex")ever returned an odd-length string,new Uint8Array(hex.length / 2)truncates and the trailing nibble is silently dropped, producing wrong bytes. The mismatch then surfaces asUnknownIdentityErrorrather than a parse failure, which is misleading to debug. Probability is low (the SDK encoder is deterministic), but the asymmetry withtryDecodeKeyDatais easy to fix.♻️ Proposed fix
if (typeof candidate.toString === "function") { try { const hex = candidate.toString("hex"); - if (typeof hex === "string" && /^[0-9a-fA-F]+$/.test(hex)) { + if ( + typeof hex === "string" && + hex.length % 2 === 0 && + /^[0-9a-fA-F]+$/.test(hex) + ) { const bytes = new Uint8Array(hex.length / 2);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/src/dash/loginWithPrivateKey.ts` around lines 291 - 304, The hex-path in loginWithPrivateKey.ts currently validates only the hex alphabet but not length, so add the same even-length guard used in tryDecodeKeyData: after computing const hex = candidate.toString("hex"), check hex.length % 2 === 0 before allocating the Uint8Array and parsing; if the length is odd, fall through or return a parse failure instead of silently truncating. Locate the block quoting candidate.toString, the hex variable and the Uint8Array creation to add this check.example-apps/dashnote/src/hooks/useWifPreview.ts (1)
39-44: 💤 Low valueModule-scoped cache is unbounded — fine for short sessions, worth bounding for long-lived tabs.
previewCacheis aMap<string, WifPreviewState>that's never evicted (only_resetWifPreviewCacheForTestsclears it). Each unique pasted WIF accumulates an entry for the lifetime of the page. In practice users don't paste many WIFs, so the leak is small, but a simple bounded LRU (or just capping at e.g. 32 entries with a FIFO eviction) would prevent surprises in long-running tabs. Optional.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/src/hooks/useWifPreview.ts` around lines 39 - 44, The module-scoped previewCache (Map<string, WifPreviewState>) is unbounded and can grow for long-lived tabs; change it to a bounded cache by implementing LRU or simple FIFO eviction (suggest cap = 32). Specifically, replace or wrap previewCache logic so that whenever an entry is inserted (wherever previewCache.set(...) is called in useWifPreview.ts) you enforce the cap: if size >= 32 evict the least-recent (LRU) or oldest key before inserting the new entry; keep the _resetWifPreviewCacheForTests() helper to clear the cache for tests and ensure WifPreviewState values are preserved during eviction bookkeeping.example-apps/dashnote/test/SessionContext.test.tsx (1)
34-40: 💤 Low valueMocked error classes diverge slightly from the real shapes.
The real
WrongKeyPurposeErrorcarriesidentityId,purposeName, andsecurityLevelName, andKeyDisabledErrorcarriesidentityId. The bareextends Error {}mocks here are fine for the current SessionContext branch (which appears to dispatch on message text), but if SessionContext ever starts reading those fields the tests will silently keep passing onundefined. Consider mirroring the real constructors so the mocks fail loudly if a future refactor depends on those fields.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/test/SessionContext.test.tsx` around lines 34 - 40, The mock error classes in the vi.mock for "../src/dash/loginWithPrivateKey" should mirror the real error shapes so tests fail loudly if code reads their fields: update the mock classes WrongKeyPurposeError and KeyDisabledError (and optionally UnknownIdentityError/InvalidPrivateKeyError) to accept the same constructor parameters and assign properties (e.g., WrongKeyPurposeError(identityId, purposeName, securityLevelName) and KeyDisabledError(identityId)) onto the instance, and keep mockLoginWithPrivateKey unchanged; locate these class definitions in the vi.mock block and add matching constructors that set the fields used by SessionContext.example-apps/dashnote/test/useWifPreview.test.tsx (1)
105-115: 💤 Low valueDocument
flushDebounce's coupling to internal microtask depth.
await Promise.resolve()twice happens to match the current resolver→DPNS chain. If the hook later inserts anotherawait(e.g., adds an additional intermediate step before settling state), this helper will silently stop flushing far enough and the tests after it will see stale state. Worth either adding a brief note that the count must match the longest internal await chain, or replacing with a small loop until a quiescent state is observed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/test/useWifPreview.test.tsx` around lines 105 - 115, The helper flushDebounce is tightly coupled to the hook's current internal microtask depth because it uses two explicit await Promise.resolve() calls; either add a clear comment above flushDebounce stating that the number of awaits must match the longest internal await chain in the hook, or (preferably) replace the two fixed awaits with a short loop that awaits Promise.resolve() repeatedly until the component/test state is observed to be stable (break after a safe max iterations like 10 to avoid infinite loops); reference the flushDebounce function and the tests that call it when implementing this change so the loop checks the same observable (DOM/state) the tests rely on to detect quiescence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@example-apps/dashnote/src/hooks/useWifPreview.ts`:
- Around line 1-11: The static imports KeyDisabledError, resolveIdentityFromWif,
UnknownIdentityError, and WrongKeyPurposeError from
"../dash/loginWithPrivateKey" (and resolveDpnsName if it triggers the SDK) pull
the ~8MB WASM SDK into the app shell; move those imports into the effect in
useWifPreview (the effect that runs when a user pastes a WIF) and replace them
with dynamic import() calls so the SDK is fetched only on first
use—specifically, inside the effect await import("../dash/loginWithPrivateKey")
to get
KeyDisabledError/resolveIdentityFromWif/UnknownIdentityError/WrongKeyPurposeError
(and await import("../dash/resolveDpnsName") if resolveDpnsName is used there),
then call resolveIdentityFromWif and handle the errors as before; keep
looksLikeWif as a static import since it’s small and used during render.
In `@example-apps/dashnote/src/session/SessionContext.tsx`:
- Around line 16-19: The static import of loginWithPrivateKey in SessionContext
pulls the large WASM SDK at module-evaluation time; instead remove the top-level
import and dynamically import the module inside the WIF branch of the login
function (the branch that handles wallet import format), e.g. use await
import("../dash/loginWithPrivateKey") within login so the heavy `@dashevo/evo-sdk`
bundle is only loaded on first auth; keep loadSdkModule() usage for other flows
and ensure types (DashKeyManager, DashSdk) remain via type imports only.
---
Outside diff comments:
In `@example-apps/dashnote/src/session/SessionContext.tsx`:
- Line 74: The public SessionValue.login type is incorrect: it names the
parameter "mnemonic" but the implementation accepts a generic secret and calls
detectSecretShape; update the context signature for SessionValue.login to use
secret instead of mnemonic (i.e. login: (secret: string, options?: LoginOptions)
=> Promise<void>) so callers and docs match the implementation that dispatches
via detectSecretShape; make the same parameter rename in any exported
types/interfaces that reference SessionValue.login to keep types consistent.
---
Nitpick comments:
In `@example-apps/dashnote/src/dash/loginWithPrivateKey.ts`:
- Around line 84-117: The function tryDecodeKeyData currently attempts hex
decoding first then falls back to base64, but the block comment claims base64 is
tried first; fix by either swapping the decode order in tryDecodeKeyData so
base64 is attempted before the hex-regex branch, or update the block comment to
state that hex is checked first (because of the strict hex-regex) and base64 is
the fallback—ensure the comment and the implementation for tryDecodeKeyData
remain consistent.
- Around line 291-304: The hex-path in loginWithPrivateKey.ts currently
validates only the hex alphabet but not length, so add the same even-length
guard used in tryDecodeKeyData: after computing const hex =
candidate.toString("hex"), check hex.length % 2 === 0 before allocating the
Uint8Array and parsing; if the length is odd, fall through or return a parse
failure instead of silently truncating. Locate the block quoting
candidate.toString, the hex variable and the Uint8Array creation to add this
check.
In `@example-apps/dashnote/src/hooks/useWifPreview.ts`:
- Around line 39-44: The module-scoped previewCache (Map<string,
WifPreviewState>) is unbounded and can grow for long-lived tabs; change it to a
bounded cache by implementing LRU or simple FIFO eviction (suggest cap = 32).
Specifically, replace or wrap previewCache logic so that whenever an entry is
inserted (wherever previewCache.set(...) is called in useWifPreview.ts) you
enforce the cap: if size >= 32 evict the least-recent (LRU) or oldest key before
inserting the new entry; keep the _resetWifPreviewCacheForTests() helper to
clear the cache for tests and ensure WifPreviewState values are preserved during
eviction bookkeeping.
In `@example-apps/dashnote/test/SessionContext.test.tsx`:
- Around line 34-40: The mock error classes in the vi.mock for
"../src/dash/loginWithPrivateKey" should mirror the real error shapes so tests
fail loudly if code reads their fields: update the mock classes
WrongKeyPurposeError and KeyDisabledError (and optionally
UnknownIdentityError/InvalidPrivateKeyError) to accept the same constructor
parameters and assign properties (e.g., WrongKeyPurposeError(identityId,
purposeName, securityLevelName) and KeyDisabledError(identityId)) onto the
instance, and keep mockLoginWithPrivateKey unchanged; locate these class
definitions in the vi.mock block and add matching constructors that set the
fields used by SessionContext.
In `@example-apps/dashnote/test/useWifPreview.test.tsx`:
- Around line 105-115: The helper flushDebounce is tightly coupled to the hook's
current internal microtask depth because it uses two explicit await
Promise.resolve() calls; either add a clear comment above flushDebounce stating
that the number of awaits must match the longest internal await chain in the
hook, or (preferably) replace the two fixed awaits with a short loop that awaits
Promise.resolve() repeatedly until the component/test state is observed to be
stable (break after a safe max iterations like 10 to avoid infinite loops);
reference the flushDebounce function and the tests that call it when
implementing this change so the loop checks the same observable (DOM/state) the
tests rely on to detect quiescence.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 05fe77bf-9740-4635-bff8-cc92336b0706
📒 Files selected for processing (12)
example-apps/dashnote/src/components/LoginModal.tsxexample-apps/dashnote/src/dash/loginWithPrivateKey.tsexample-apps/dashnote/src/dash/types.tsexample-apps/dashnote/src/hooks/useWifPreview.tsexample-apps/dashnote/src/lib/detectSecretShape.tsexample-apps/dashnote/src/session/SessionContext.tsxexample-apps/dashnote/src/session/keyManagerFromKey.tsexample-apps/dashnote/test/LoginModal.test.tsxexample-apps/dashnote/test/SessionContext.test.tsxexample-apps/dashnote/test/detectSecretShape.test.tsexample-apps/dashnote/test/loginWithPrivateKey.test.tsexample-apps/dashnote/test/useWifPreview.test.tsx
The eager-preview hook and SessionContext were statically importing loginWithPrivateKey, which transitively pulls @dashevo/evo-sdk (~8MB WASM) into the entry chunk and defeats the lazy-load strategy. Switch both to dynamic imports gated behind the post-debounce timer (hook) and the WIF login branch (context); rename SessionValue.login's parameter mnemonic→secret to match the implementation since the prior WIF-login PR. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@example-apps/dashnote/src/hooks/useWifPreview.ts`:
- Around line 42-45: The hook currently caches raw WIF strings (previewCache
keyed by the full WIF and Resolved.wif stored in state), which retains secrets
beyond the form lifecycle; change useWifPreview so previews are only kept
component-local and cleared whenever the input WIF changes or modal closes: stop
storing the raw WIF in previewCache and in Resolved.wif, instead cache by a
non-secret derivative (e.g., a hash/nonce) or remove caching entirely, keep only
the preview state (WifPreviewState) tied to the current input, and ensure any
existing previewCache entries are cleared on input change or unmount in the hook
(reference previewCache, Resolved, resolved.wif, and useWifPreview to locate and
fix).
- Around line 91-149: The lazy-import (loadLoginModule) is awaited outside the
try, so import/chunk fetch failures escape and leave the preview stuck in
"checking"; move the await of loadLoginModule() into the existing try block (or
wrap it in its own try/catch) so any errors from loadLoginModule are caught and
collapse to the same idle/silent outcome (set next = IDLE, avoid caching, and
return via the same cancelled checks), keeping references to loadLoginModule,
mod.resolveIdentityFromWif, previewCache, and setResolved to locate the change.
In `@example-apps/dashnote/src/session/SessionContext.tsx`:
- Around line 216-229: The catch path currently prefers loadRememberedIdentity()
and overwrites the live session snapshot, causing an authenticated user to be
forced into "browsing"; change the logic in SessionContext so you only apply the
remembered identity when there is no valid prior session: check priorKeyManager
(and/or priorStatus not indicating an authenticated session) before calling
setKeyManager/setIdentityId/setDpnsName/setStatus with the remembered values;
otherwise restore the prior snapshot by assigning priorKeyManager,
priorIdentityId, priorDpnsName, and priorStatus back to the state variables
instead of switching to "browsing" (use the existing symbols
loadRememberedIdentity, priorKeyManager, priorIdentityId, priorDpnsName,
priorStatus, setKeyManager, setIdentityId, setDpnsName, setStatus).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8d553c2d-04b7-4adf-a04c-70fc44860da2
📒 Files selected for processing (2)
example-apps/dashnote/src/hooks/useWifPreview.tsexample-apps/dashnote/src/session/SessionContext.tsx
- Drop the module-scoped previewCache in useWifPreview: cached raw WIFs survived modal close/reopen for the page lifetime, extending secret retention beyond the form. Resolution state is now strictly component-local. - Move the loadLoginModule() await inside the try in useWifPreview so a chunk-fetch failure collapses to idle instead of stranding the preview on "checking". - Restore the active session in SessionContext's login catch path when priorStatus is "authenticated": typing a bad secret while signed in no longer demotes the user to browsing, even when a remembered identity is on disk. Switch flows already logout() before login() and are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 Nitpick comments (2)
example-apps/dashnote/src/session/SessionContext.tsx (1)
188-189: 💤 Low valueRedundant nullish-coalesce.
resolvedIdis already... ?? null, so the second?? nullon line 189 is a no-op.♻️ Proposed tweak
const resolvedId = resolvedKeyManager.identityId ?? null; - setIdentityId(resolvedId ?? null); + setIdentityId(resolvedId);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/src/session/SessionContext.tsx` around lines 188 - 189, The code creates resolvedId using resolvedKeyManager.identityId ?? null and then calls setIdentityId(resolvedId ?? null), which is redundant; change the setIdentityId call to pass resolvedId directly (or inline the expression once) so you remove the duplicate "?? null" — update the usage in SessionContext (resolvedId / setIdentityId) accordingly.example-apps/dashnote/test/useWifPreview.test.tsx (1)
1-327: 💤 Low valueLGTM — thorough state-by-state coverage with proper fake-timer discipline.
The use of real-shaped error classes via
vi.hoisted(rather than bareextends Error) is the right call since the hook relies oninstanceof mod.WrongKeyPurposeError/mod.KeyDisabledErrorchecks. The debounce-cancellation, whitespace-trim, and cross-mount-no-cache tests are particularly valuable for locking in the secret-retention guarantees the hook is responsible for.One coverage gap worth considering (optional): the hook explicitly handles
loadLoginModule()rejection (chunk fetch failure / offline) by falling through to IDLE via themod && ...guard at lines 103-104. There's no test that exercises that branch, so a future refactor that hoistsloadLoginModule()back outside thetry(the original bug) wouldn't be caught here. A small test that mocks the dynamic import to reject could lock that in, but it requiresvi.mockwith a rejecting factory or module-cache manipulation, so it may not be worth the friction.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@example-apps/dashnote/test/useWifPreview.test.tsx` around lines 1 - 327, Add a test that exercises the hook path where the dynamic import/loadLoginModule rejects so the hook falls back to idle; specifically, mock the module load to throw (simulate chunk fetch/offline) and then render Harness and flushDebounce, asserting the final state is "idle" and that mockResolveIdentityFromWif and mockResolveDpnsName were not called. Target the useWifPreview behavior and the dynamic import/loader (loadLoginModule) branch — ensure the mock makes the importer reject before the hook attempts resolution so the guard that checks the loaded module is exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@example-apps/dashnote/src/session/SessionContext.tsx`:
- Around line 188-189: The code creates resolvedId using
resolvedKeyManager.identityId ?? null and then calls setIdentityId(resolvedId ??
null), which is redundant; change the setIdentityId call to pass resolvedId
directly (or inline the expression once) so you remove the duplicate "?? null" —
update the usage in SessionContext (resolvedId / setIdentityId) accordingly.
In `@example-apps/dashnote/test/useWifPreview.test.tsx`:
- Around line 1-327: Add a test that exercises the hook path where the dynamic
import/loadLoginModule rejects so the hook falls back to idle; specifically,
mock the module load to throw (simulate chunk fetch/offline) and then render
Harness and flushDebounce, asserting the final state is "idle" and that
mockResolveIdentityFromWif and mockResolveDpnsName were not called. Target the
useWifPreview behavior and the dynamic import/loader (loadLoginModule) branch —
ensure the mock makes the importer reject before the hook attempts resolution so
the guard that checks the loaded module is exercised.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f3b7a23b-5210-4038-babd-f152ffd2cb14
📒 Files selected for processing (4)
example-apps/dashnote/src/hooks/useWifPreview.tsexample-apps/dashnote/src/session/SessionContext.tsxexample-apps/dashnote/test/SessionContext.test.tsxexample-apps/dashnote/test/useWifPreview.test.tsx
Summary
resolveIdentityFromWifout ofloginWithPrivateKeyso the preview path can validate without touching the WASM signer;WrongKeyPurposeErrornow also carriessecurityLevelName.UnknownIdentityErrorand transient errors stay silent in the preview (no pre-submit error UI); the actual login still surfaces them on submit.Summary by CodeRabbit
New Features
Tests