refactor(web): unify per-type item field selection behind ItemDescriptor#68
Merged
Conversation
The "which fields matter for type X" decision was re-encoded in four places:
the vault-list search switch (route.tsx), getItemSubtitle (ItemCard.tsx), the
read-only detail renderer, and the form field registry. Search and subtitle
carried `as string` casts because the item type was unknown at the call site,
and neither was directly tested.
Deepen the existing form registry with a pure, React-free sibling:
ITEM_DESCRIPTORS in item-fields/descriptors.ts. Each entry is keyed by
VaultItem['type'] and typed against Extract<VaultItem, {type: K}>, so field
access needs no casts; the two dispatchers (getItemSearchText,
getItemSubtitle) carry a single contained assertion and return '' for unknown
types. Kept separate from index.ts so the hot list-filter path does not pull
the field-editor component graph into its bundle. getItemSubtitle is
re-exported from ItemCard.tsx so existing import paths and the trash test mock
keep working.
Invariant, locked by a table-driven test: the search haystack emits no secret
material (password, number, cvv, secret, clientSecret, privateKey, passphrase,
mnemonic) and excludes the generic title. Detail renderer and TYPE_OPTIONS are
left for a follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The "which fields matter for type X" decision was re-encoded in four places:
route.tsx, 44-line per-typeswitch)getItemSubtitle(ItemCard.tsx)$itemId.tsx)item-fields/index.ts)Search + subtitle carried
as stringcasts (item type unknown at the call site) and neither was directly tested.Change
Deepen the form registry with a pure, React-free sibling —
ITEM_DESCRIPTORSinitem-fields/descriptors.ts:VaultItem['type'], typed againstExtract<VaultItem, {type: K}>→ no casts inside entries.getItemSearchText/getItemSubtitlecarry the one contained assertion and return''for unknown types (preserves the olddefaultsafety).index.tsso the hot list-filter path does not pull the field-editor component graph into its bundle.getItemSubtitlere-exported fromItemCard.tsx→ existing import paths + the trash test mock keep working (zero caller churn).route.tsxswaps its 44-line switch foritem.title... || getItemSearchText(item)....Invariant (locked by test)
The search haystack is a plaintext index —
toSearchTextemits no secret material (password,number,cvv,secret,clientSecret,privateKey,passphrase,mnemonic) and excludes the generictitle. A table-driven test asserts this across all 7 types +developer_credential×3 modes +crypto_wallet×2 + an unknown type.Scope
Detail renderer (
ReadComponent) and TypeFilterTYPE_OPTIONSare deliberately left as the obvious next descriptor fields (noted inCONTEXT.md).Verification
descriptors.test.ts— 31/31 passtsc -bclean · eslint cleanNet −76 / +15 in touched source; previously-untested search + subtitle logic now sits behind one tested interface.
🤖 Generated with Claude Code