feat: implement sync conflict resolver with timestamp-based merging

Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-12 07:49:13 +00:00
parent afaf580a2b
commit 82c537d659
2 changed files with 133 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
import { BibleHighlight } from '@/types'
/**
* Resolves conflicts between client and server versions of a highlight.
* Uses timestamp-based "last write wins" strategy.
*/
export function resolveConflict(
clientVersion: BibleHighlight,
serverVersion: BibleHighlight
): BibleHighlight {
// Use timestamp to determine which version is newer
const isServerNewer = serverVersion.updatedAt > clientVersion.updatedAt
// Take the newer version and mark as synced
const resolvedVersion = isServerNewer ? serverVersion : clientVersion
return {
...resolvedVersion,
syncStatus: 'synced' as const
}
}
/**
* Merges server highlights with client highlights.
* - Adds new highlights from server
* - Updates existing highlights if server version is newer
* - Keeps client highlights if client version is newer
*/
export function mergeHighlights(
clientHighlights: BibleHighlight[],
serverHighlights: BibleHighlight[]
): BibleHighlight[] {
const clientMap = new Map(clientHighlights.map(h => [h.id, h]))
const serverMap = new Map(serverHighlights.map(h => [h.id, h]))
const merged = new Map<string, BibleHighlight>()
// Add all client highlights, resolving conflicts with server
for (const [id, clientH] of clientMap) {
const serverH = serverMap.get(id)
if (serverH) {
// Conflict: both have this highlight
merged.set(id, resolveConflict(clientH, serverH))
} else {
// No conflict: only client has it
merged.set(id, clientH)
}
}
// Add any server highlights not in client
for (const [id, serverH] of serverMap) {
if (!clientMap.has(id)) {
merged.set(id, { ...serverH, syncStatus: 'synced' as const })
}
}
return Array.from(merged.values())
}