refactor(frontend): Split friends.ts store into focused modules#120
refactor(frontend): Split friends.ts store into focused modules#120enko merged 2 commits intorefactor/store-action-wrapperfrom
Conversation
Splits the 806-line friends.ts into three focused files: - friends.ts (194 lines) — core friend CRUD, derived stores, re-exports - friendSubresources.ts (542 lines) — all subresource CRUD operations - friendListFilter.ts (94 lines) — filter/search state persistence All existing imports from '$lib/stores/friends' continue to work unchanged via re-exports. No consumer changes needed. Closes #113 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| @@ -0,0 +1,94 @@ | |||
| import { derived, writable } from 'svelte/store'; | |||
There was a problem hiding this comment.
The buildSearchParams and parseSearchParams methods take an external FriendListFilterState argument rather than reading the store's own current state. This creates an unusual API where callers must subscribe and pass state back in manually. Consider either (a) making these pure utility functions exported separately from the store, or (b) using Svelte's get(friendListFilter) internally so callers invoke them without arguments. The current design leaks internal state shape to callers unnecessarily.
| @@ -0,0 +1,94 @@ | |||
| import { derived, writable } from 'svelte/store'; | |||
| import type { FacetFilters } from '$shared'; | |||
|
|
|||
There was a problem hiding this comment.
The FriendListFilterState interface is not exported. Consumers who want to type-annotate variables holding a snapshot of the filter state (e.g. in Svelte components using let state: FriendListFilterState) cannot do so without re-defining the type. Consider exporting it.
| Address, | ||
| AddressInput, | ||
| CircleSummary, | ||
| DateInput, |
There was a problem hiding this comment.
The FriendsUpdate type is defined as (fn: (state: FriendsState) => FriendsState) => void, but storeAction (and Svelte's writable update) actually spreads a Partial<FriendsState> onto the current state, meaning the callback only returns a partial. This is safe at runtime because of the spread in storeAction, but the type could be more accurately described as (fn: (state: FriendsState) => Partial<FriendsState>) => void to match actual usage. As written, TypeScript will reject callbacks that return only a partial object — though since storeAction already types it correctly via S extends { isLoading: boolean; error: string | null }, this is an edge case that may not surface in practice.
🔍 Automated Code Review
What Changed Since Last ReviewCommit
Remaining Concerns
|
| Severity | Count |
|---|---|
| 🚨 Critical | 0 |
| 2 (missing tests, unsafe cast) | |
| 💡 Suggestion | 0 |
AGENTS.md Compliance
- ✅ Architecture patterns — clean module split with backward-compatible re-exports
- ✅ TypeScript strict patterns — no
anytypes, proper type exports ⚠️ Testing standards — no tests added for newfriendListFilter.tsutility functions (>80% target)
Summary
The refactor is well-structured and the previous review's two main design issues have been addressed. The remaining concerns are: (1) unit tests for buildSearchParams/parseSearchParams per AGENTS.md coverage requirements, and (2) the unsafe as cast for relationship_category parsing. Neither is a blocking correctness issue if API validation can be relied upon, but the test gap is a policy concern.
🤖 Automated review by Claude Code • View CI Run
There was a problem hiding this comment.
This is a well-executed refactor that successfully splits an 806-line monolithic store into three focused modules with zero consumer-facing changes. The main actionable items are: (1) the buildSearchParams/parseSearchParams API design which leaks internal state shape to callers; (2) the unexported FriendListFilterState interface; (3) missing unit tests for the new modules, particularly the URL parsing logic in friendListFilter.ts; and (4) an unsafe as cast for relationship_category that could silently accept invalid enum values from URL parameters. Requesting changes primarily for the missing tests (per project's stated >80% coverage requirement) and the API design concern.
- Export FriendListFilterState interface for consumer type annotations - Extract buildSearchParams/parseSearchParams as standalone pure utility functions instead of store methods (cleaner API, no need to pass state back to the store) - Re-export new symbols from friends.ts barrel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Summary
friends.tsinto three focused files:friends.ts(194 lines) — core friend CRUD, derived stores, re-exportsfriendSubresources.ts(542 lines) — all subresource CRUD operations (phones, emails, addresses, URLs, photos, dates, met info, social profiles, professional history, relationships, circles)friendListFilter.ts(94 lines) — filter/search state persistence$lib/stores/friendscontinue to work via re-exports — zero consumer changes neededTest plan
$lib/stores/friendsresolve correctlyDepends on #119
Closes #113
🤖 Generated with Claude Code