Enhances autosave controller with missing features:
- prime(payload): Sets initial hash baseline to prevent autosaves on page load
- idleResetMs option: Auto-transitions from 'saved' → 'idle' status (default 2s)
- onSaved callback: Now receives { prime } helper for re-priming after server response
- Cleanup: destroy() now properly clears idle reset timer
All existing tests pass. Backward compatible - forms not using new features yet.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
6.3 KiB
Task 6: Autosave Store Implementation Plan
Goal
Modernize autosave to use Svelte 5 runes while fixing existing bugs. Ensure data integrity through incremental implementation with validation points.
Overview
Current State:
createAutoSaveController()uses manual subscriptions (Svelte 4 pattern)- Works in ProjectForm and partially in posts editor
- Has known bugs: autosaves on load, broken navigation guard, status doesn't reset to idle
Target State:
createAutoSaveStore()using Svelte 5$state()runes- Fixes known bugs (prime baseline, auto-idle, navigation guard)
- Clean API:
autoSave.statusinstead ofautoSave.status.subscribe(...) - Reusable across all admin forms
Implementation Steps
Step 1: Add Missing Features to Current Controller
Why first: Existing tests already expect these features. Fix bugs before converting to runes.
Changes to src/lib/admin/autoSave.ts:
- Add
prime(payload)method to set initial hash baseline (prevents autosave on load) - Add
idleResetMsoption for auto-transition: 'saved' → 'idle' (default 2000ms) - Enhance
onSavedcallback to receive{ prime }helper for re-priming after server response
Validation:
node --test --loader tsx tests/autoSaveController.test.ts
All 3 tests should pass.
Quick Manual Test:
- Open browser console on ProjectForm
- Verify no PUT request fires on initial load
- Make an edit, verify save triggers after 2s
Step 2: Convert to Runes-Based Store
Why separate: Proves the rune conversion without complicating Step 1's bug fixes.
Changes:
- Rename:
src/lib/admin/autoSave.ts→src/lib/admin/autoSave.svelte.ts - Replace manual subscriptions with rune-based state:
let status = $state<AutoSaveStatus>('idle') let lastError = $state<string | null>(null) return { get status() { return status }, get lastError() { return lastError }, schedule, flush, destroy, prime } - Export types:
AutoSaveStore,AutoSaveStoreOptions
Validation:
npm run check # Should pass (ignore pre-existing errors)
Create minimal test component:
<script>
import { createAutoSaveStore } from '$lib/admin/autoSave.svelte'
const store = createAutoSaveStore({ ... })
</script>
<div>Status: {store.status}</div>
Verify status updates reactively without manual subscription.
Step 3: Update ProjectForm (Pilot)
Why ProjectForm first: It's the most complex form. If it works here, others will be easier.
Changes to src/lib/components/admin/ProjectForm.svelte:
- Import new store:
import { createAutoSaveStore } from '$lib/admin/autoSave.svelte' - Remove subscription code (if any exists)
- Add
hasLoadedflag:let hasLoaded = $state(false) - After
populateFormData()completes:formData = { ...loadedData } autoSave?.prime(buildPayload()) hasLoaded = true - Update
$effectthat schedules autosave:$effect(() => { formData // establish dependency if (mode === 'edit' && hasLoaded && autoSave) { autoSave.schedule() if (draftKey) saveDraft(draftKey, buildPayload()) } }) - Use lifecycle helper (if not already):
import { initAutoSaveLifecycle } from '$lib/admin/autoSaveLifecycle' if (mode === 'edit' && autoSave) { initAutoSaveLifecycle(autoSave, { isReady: () => hasLoaded, onFlushError: (error) => console.error('Autosave flush failed:', error) }) }
Critical Validation Checklist:
- Open existing project → no autosave fires
- Edit title → autosave triggers after 2s
- Status shows: idle → saving → saved → idle
- Make edit, navigate away → save completes first
- Press Cmd/Ctrl+S → immediate save
- Make edit, refresh page → draft prompt appears
- Restore draft, make manual save → draft clears
Debugging:
- Network tab: Watch for PUT requests to
/api/projects/{id} - Console: Add
console.log('Saving:', payload)in save function - Console: Add
console.log('Status:', store.status)to watch transitions
Step 4: Update Posts Editor
Apply same pattern to src/routes/admin/posts/[id]/edit/+page.svelte
Key differences:
- Simpler structure (no case study)
- Add missing
restoreDraft()anddismissDraft()functions (currently referenced but not defined)
Validation: Same checklist as ProjectForm
Step 5: Update Remaining Forms (Optional)
If EssayForm, PhotoPostForm, SimplePostForm use autosave, apply same pattern.
Validation: Quick smoke test (edit, save, verify no errors)
Step 6: Update Tests & Cleanup
- Rename test file:
tests/autoSaveController.test.ts→tests/autoSaveStore.test.ts - Update imports in test file
- Run tests:
node --test --loader tsx tests/autoSaveStore.test.ts - Update
docs/autosave-completion-guide.mdto reflect new API
Data Integrity Safeguards
Hash-Based Deduplication
✓ Only saves when payload changes (via JSON hash comparison)
Concurrency Control
✓ updatedAt field prevents overwriting newer server data
Request Cancellation
✓ AbortController cancels in-flight requests when new save triggered
Navigation Guard
✓ Waits for flush to complete before allowing route change
Draft Recovery
✓ localStorage backup in case of crash/accidental navigation
Rollback Strategy
If issues in Step 1: Revert autoSave.ts changes
If issues in Step 2: Keep Step 1 fixes, revert rune conversion
If issues in Step 3: Only ProjectForm affected, other forms unchanged
If issues in Step 4+: Revert individual forms independently
Success Criteria
- ✅ No autosaves on initial page load
- ✅ Saves trigger correctly on edits (2s debounce)
- ✅ Status indicator cycles properly (idle → saving → saved → idle)
- ✅ Navigation guard prevents data loss
- ✅ Draft recovery works reliably
- ✅ All unit tests pass
- ✅ Zero duplicate save requests
- ✅ Manual QA checklist passes
Notes
- Keep old
autoSave.tsuntil all forms migrate (backward compatibility) - Test with slow network (Chrome DevTools → Network → Slow 3G)
- Test offline mode (DevTools → Network → Offline)
- Each step is independently testable
- Stop at any step if issues arise