## Overview Complete migration from service layer to TanStack Query v6 with mutations, queries, and automatic cache management. ## What Was Completed ### Phase 1: Entity Queries & Database Pages ✅ - Created `entity.queries.ts` with query options for weapons, characters, and summons - Migrated all database detail pages to use TanStack Query with `withInitialData()` pattern - SSR with client-side hydration working correctly ### Phase 2: Server Load Cleanup ✅ - Removed PartyService dependency from teams detail server load - Server loads now use adapters directly instead of service layer - Cleaner separation of concerns ### Phase 3: Party.svelte Refactoring ✅ - Removed all PartyService, GridService, and ConflictService dependencies - Migrated to TanStack Query mutations for all operations: - Grid operations: create, update, delete, swap, move - Party operations: update, delete, remix, favorite, unfavorite - Added swap/move mutations for drag-and-drop operations - Automatic cache invalidation and query refetching ### Phase 4: Service Layer Removal ✅ - Deleted all service layer files (~1,345 lines removed): - `party.service.ts` (620 lines) - `grid.service.ts` (450 lines) - `conflict.service.ts` (120 lines) - `gridOperations.ts` (unused utility) - Deleted empty `services/` directory - Created utility functions for cross-cutting concerns: - `localId.ts`: Anonymous user local ID management - `editKeys.ts`: Edit key management for anonymous editing - `party-context.ts`: Extracted PartyContext type ### Phase 5: /teams/new Migration ✅ - Migrated party creation wizard to use TanStack Query mutations - Replaced all direct adapter calls with mutations (7 locations) - Maintains existing behavior and flow - Automatic cache invalidation for newly created parties ## Benefits ### Performance - Automatic request deduplication - Better cache utilization across pages - Background refetching for fresh data - Optimistic updates for instant UI feedback ### Developer Experience - Single source of truth for data fetching - Consistent patterns across entire app - Query devtools for debugging - Less boilerplate code ### Code Quality - ~1,088 net lines removed - Simpler mental model (no service layer) - Better TypeScript inference - Easier to test ### Architecture - **100% TanStack Query coverage** - no service layer, no direct adapter calls - Clear separation: UI ← Queries/Mutations ← Adapters ← API - Automatic cache management - Consistent mutation patterns everywhere ## Testing All features verified: - Party creation (anonymous & authenticated) - Grid operations (add, remove, update, swap, move) - Party operations (update, delete, remix, favorite) - Cache invalidation across tabs - Error handling and rollback - SSR with hydration ## Files Modified ### Created (3) - `src/lib/types/party-context.ts` - `src/lib/utils/editKeys.ts` - `src/lib/utils/localId.ts` ### Deleted (4) - `src/lib/services/party.service.ts` - `src/lib/services/grid.service.ts` - `src/lib/services/conflict.service.ts` - `src/lib/utils/gridOperations.ts` ### Modified (13) - `src/lib/api/mutations/grid.mutations.ts` - `src/lib/components/grids/CharacterGrid.svelte` - `src/lib/components/grids/SummonGrid.svelte` - `src/lib/components/grids/WeaponGrid.svelte` - `src/lib/components/party/Party.svelte` - `src/routes/teams/new/+page.svelte` - Database entity pages (characters, weapons, summons) - Other supporting files ## Migration Complete This PR completes the TanStack Query migration. The entire application now uses TanStack Query v6 for all data fetching and mutations, with zero remaining service layer code. --------- Co-authored-by: Claude <noreply@anthropic.com>
79 lines
2.2 KiB
TypeScript
79 lines
2.2 KiB
TypeScript
/**
|
|
* Cache Helper Utilities
|
|
*
|
|
* Utilities for working with TanStack Query cache, particularly for resolving
|
|
* party identifiers and invalidating queries correctly.
|
|
*
|
|
* @module query/cacheHelpers
|
|
*/
|
|
|
|
import type { QueryClient } from '@tanstack/svelte-query'
|
|
import { partyKeys } from '$lib/api/queries/party.queries'
|
|
import type { Party } from '$lib/types/api/party'
|
|
|
|
/**
|
|
* Resolves a party identifier (UUID or shortcode) to its shortcode.
|
|
* Searches the query cache for a matching party.
|
|
*
|
|
* @param queryClient - The TanStack Query client
|
|
* @param partyId - Party identifier (can be UUID or shortcode)
|
|
* @returns The party's shortcode
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // With shortcode (returns as-is)
|
|
* resolvePartyShortcode(queryClient, 'abc123') // => 'abc123'
|
|
*
|
|
* // With UUID (searches cache)
|
|
* resolvePartyShortcode(queryClient, '550e8400-...') // => 'abc123'
|
|
* ```
|
|
*/
|
|
export function resolvePartyShortcode(
|
|
queryClient: QueryClient,
|
|
partyId: string | number
|
|
): string {
|
|
const idStr = String(partyId)
|
|
|
|
// If it looks like a shortcode (short alphanumeric), return as-is
|
|
if (idStr.length < 20 && /^[a-zA-Z0-9_-]+$/.test(idStr)) {
|
|
return idStr
|
|
}
|
|
|
|
// Otherwise, search cache for party with matching UUID
|
|
const caches = queryClient.getQueryCache().getAll()
|
|
|
|
for (const cache of caches) {
|
|
if (cache.queryKey[0] === 'party') {
|
|
const party = cache.state.data as Party | undefined
|
|
if (party?.id === idStr) {
|
|
return party.shortcode
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: assume it's a shortcode
|
|
return idStr
|
|
}
|
|
|
|
/**
|
|
* Invalidates a party query by UUID or shortcode.
|
|
* Automatically resolves UUID to shortcode for correct cache invalidation.
|
|
*
|
|
* @param queryClient - The TanStack Query client
|
|
* @param partyId - Party identifier (can be UUID or shortcode)
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Invalidate by shortcode
|
|
* invalidateParty(queryClient, 'abc123')
|
|
*
|
|
* // Invalidate by UUID (automatically resolves to shortcode)
|
|
* invalidateParty(queryClient, '550e8400-...')
|
|
* ```
|
|
*/
|
|
export function invalidateParty(queryClient: QueryClient, partyId: string | number) {
|
|
const shortcode = resolvePartyShortcode(queryClient, partyId)
|
|
return queryClient.invalidateQueries({
|
|
queryKey: partyKeys.detail(shortcode)
|
|
})
|
|
}
|