diff options
Diffstat (limited to 'src/client/sync/conflict.ts')
| -rw-r--r-- | src/client/sync/conflict.ts | 155 |
1 files changed, 49 insertions, 106 deletions
diff --git a/src/client/sync/conflict.ts b/src/client/sync/conflict.ts index 88b5bfe..be767dc 100644 --- a/src/client/sync/conflict.ts +++ b/src/client/sync/conflict.ts @@ -41,7 +41,7 @@ import type { SyncPushResult } from "./push"; */ export interface ConflictResolutionItem { id: string; - resolution: "server_wins" | "local_wins"; + resolution: "server_wins"; } /** @@ -57,19 +57,6 @@ export interface ConflictResolutionResult { } /** - * Options for conflict resolver - */ -export interface ConflictResolverOptions { - /** - * Strategy for resolving conflicts - * - "crdt": Use Automerge CRDT merge for conflict-free resolution (default) - * - "server_wins": Always use server data (fallback when no CRDT data) - * - "local_wins": Always use local data - */ - strategy?: "crdt" | "server_wins" | "local_wins"; -} - -/** * CRDT merge result with entity data */ interface CrdtMergeConflictResult<T> { @@ -203,17 +190,11 @@ function serverNoteFieldValueToLocal( * Handles conflicts reported by the server during push operations. * When a conflict occurs (server has newer data), this resolver: * 1. Identifies conflicting items from push result - * 2. Uses Automerge CRDT merge for conflict-free resolution (default) + * 2. Uses Automerge CRDT merge for conflict-free resolution * 3. Falls back to server_wins when CRDT data is unavailable * 4. Updates local database accordingly */ export class ConflictResolver { - private strategy: "crdt" | "server_wins" | "local_wins"; - - constructor(options: ConflictResolverOptions = {}) { - this.strategy = options.strategy ?? "crdt"; - } - /** * Check if there are conflicts in push result */ @@ -243,15 +224,15 @@ export class ConflictResolver { } /** - * Resolve deck conflict using configured strategy + * Resolve deck conflict using CRDT merge with server_wins fallback */ async resolveDeckConflict( localDeck: LocalDeck, serverDeck: ServerDeck, serverCrdtBinary?: Uint8Array, ): Promise<ConflictResolutionItem> { - // Try CRDT merge first if strategy is "crdt" and we have CRDT data - if (this.strategy === "crdt" && serverCrdtBinary) { + // Try CRDT merge first if we have CRDT data + if (serverCrdtBinary) { const mergeResult = await this.mergeDeckWithCrdt( localDeck, serverCrdtBinary, @@ -273,18 +254,11 @@ export class ConflictResolver { } } - // Fallback strategy when CRDT merge is not available - const resolution = - this.strategy === "local_wins" ? "local_wins" : "server_wins"; - - if (resolution === "server_wins") { - // Update local with server data - const localData = serverDeckToLocal(serverDeck); - await localDeckRepository.upsertFromServer(localData); - } - // If local_wins, we keep local data and it will be pushed again next sync + // Fallback to server_wins when CRDT merge is not available + const localData = serverDeckToLocal(serverDeck); + await localDeckRepository.upsertFromServer(localData); - return { id: localDeck.id, resolution }; + return { id: localDeck.id, resolution: "server_wins" }; } /** @@ -327,15 +301,15 @@ export class ConflictResolver { } /** - * Resolve card conflict using configured strategy + * Resolve card conflict using CRDT merge with server_wins fallback */ async resolveCardConflict( localCard: LocalCard, serverCard: ServerCard, serverCrdtBinary?: Uint8Array, ): Promise<ConflictResolutionItem> { - // Try CRDT merge first if strategy is "crdt" and we have CRDT data - if (this.strategy === "crdt" && serverCrdtBinary) { + // Try CRDT merge first if we have CRDT data + if (serverCrdtBinary) { const mergeResult = await this.mergeCardWithCrdt( localCard, serverCrdtBinary, @@ -356,18 +330,11 @@ export class ConflictResolver { } } - // Fallback strategy when CRDT merge is not available - const resolution = - this.strategy === "local_wins" ? "local_wins" : "server_wins"; + // Fallback to server_wins when CRDT merge is not available + const localData = serverCardToLocal(serverCard); + await localCardRepository.upsertFromServer(localData); - if (resolution === "server_wins") { - // Update local with server data - const localData = serverCardToLocal(serverCard); - await localCardRepository.upsertFromServer(localData); - } - // If local_wins, we keep local data and it will be pushed again next sync - - return { id: localCard.id, resolution }; + return { id: localCard.id, resolution: "server_wins" }; } /** @@ -405,15 +372,15 @@ export class ConflictResolver { } /** - * Resolve note type conflict using configured strategy + * Resolve note type conflict using CRDT merge with server_wins fallback */ async resolveNoteTypeConflict( localNoteType: LocalNoteType, serverNoteType: ServerNoteType, serverCrdtBinary?: Uint8Array, ): Promise<ConflictResolutionItem> { - // Try CRDT merge first if strategy is "crdt" and we have CRDT data - if (this.strategy === "crdt" && serverCrdtBinary) { + // Try CRDT merge first if we have CRDT data + if (serverCrdtBinary) { const mergeResult = await this.mergeNoteTypeWithCrdt( localNoteType, serverCrdtBinary, @@ -434,16 +401,11 @@ export class ConflictResolver { } } - // Fallback strategy when CRDT merge is not available - const resolution = - this.strategy === "local_wins" ? "local_wins" : "server_wins"; + // Fallback to server_wins when CRDT merge is not available + const localData = serverNoteTypeToLocal(serverNoteType); + await localNoteTypeRepository.upsertFromServer(localData); - if (resolution === "server_wins") { - const localData = serverNoteTypeToLocal(serverNoteType); - await localNoteTypeRepository.upsertFromServer(localData); - } - - return { id: localNoteType.id, resolution }; + return { id: localNoteType.id, resolution: "server_wins" }; } /** @@ -481,15 +443,15 @@ export class ConflictResolver { } /** - * Resolve note field type conflict using configured strategy + * Resolve note field type conflict using CRDT merge with server_wins fallback */ async resolveNoteFieldTypeConflict( localFieldType: LocalNoteFieldType, serverFieldType: ServerNoteFieldType, serverCrdtBinary?: Uint8Array, ): Promise<ConflictResolutionItem> { - // Try CRDT merge first if strategy is "crdt" and we have CRDT data - if (this.strategy === "crdt" && serverCrdtBinary) { + // Try CRDT merge first if we have CRDT data + if (serverCrdtBinary) { const mergeResult = await this.mergeNoteFieldTypeWithCrdt( localFieldType, serverCrdtBinary, @@ -510,16 +472,11 @@ export class ConflictResolver { } } - // Fallback strategy when CRDT merge is not available - const resolution = - this.strategy === "local_wins" ? "local_wins" : "server_wins"; - - if (resolution === "server_wins") { - const localData = serverNoteFieldTypeToLocal(serverFieldType); - await localNoteFieldTypeRepository.upsertFromServer(localData); - } + // Fallback to server_wins when CRDT merge is not available + const localData = serverNoteFieldTypeToLocal(serverFieldType); + await localNoteFieldTypeRepository.upsertFromServer(localData); - return { id: localFieldType.id, resolution }; + return { id: localFieldType.id, resolution: "server_wins" }; } /** @@ -560,15 +517,15 @@ export class ConflictResolver { } /** - * Resolve note conflict using configured strategy + * Resolve note conflict using CRDT merge with server_wins fallback */ async resolveNoteConflict( localNote: LocalNote, serverNote: ServerNote, serverCrdtBinary?: Uint8Array, ): Promise<ConflictResolutionItem> { - // Try CRDT merge first if strategy is "crdt" and we have CRDT data - if (this.strategy === "crdt" && serverCrdtBinary) { + // Try CRDT merge first if we have CRDT data + if (serverCrdtBinary) { const mergeResult = await this.mergeNoteWithCrdt( localNote, serverCrdtBinary, @@ -589,16 +546,11 @@ export class ConflictResolver { } } - // Fallback strategy when CRDT merge is not available - const resolution = - this.strategy === "local_wins" ? "local_wins" : "server_wins"; - - if (resolution === "server_wins") { - const localData = serverNoteToLocal(serverNote); - await localNoteRepository.upsertFromServer(localData); - } + // Fallback to server_wins when CRDT merge is not available + const localData = serverNoteToLocal(serverNote); + await localNoteRepository.upsertFromServer(localData); - return { id: localNote.id, resolution }; + return { id: localNote.id, resolution: "server_wins" }; } /** @@ -636,15 +588,15 @@ export class ConflictResolver { } /** - * Resolve note field value conflict using configured strategy + * Resolve note field value conflict using CRDT merge with server_wins fallback */ async resolveNoteFieldValueConflict( localFieldValue: LocalNoteFieldValue, serverFieldValue: ServerNoteFieldValue, serverCrdtBinary?: Uint8Array, ): Promise<ConflictResolutionItem> { - // Try CRDT merge first if strategy is "crdt" and we have CRDT data - if (this.strategy === "crdt" && serverCrdtBinary) { + // Try CRDT merge first if we have CRDT data + if (serverCrdtBinary) { const mergeResult = await this.mergeNoteFieldValueWithCrdt( localFieldValue, serverCrdtBinary, @@ -665,16 +617,11 @@ export class ConflictResolver { } } - // Fallback strategy when CRDT merge is not available - const resolution = - this.strategy === "local_wins" ? "local_wins" : "server_wins"; - - if (resolution === "server_wins") { - const localData = serverNoteFieldValueToLocal(serverFieldValue); - await localNoteFieldValueRepository.upsertFromServer(localData); - } + // Fallback to server_wins when CRDT merge is not available + const localData = serverNoteFieldValueToLocal(serverFieldValue); + await localNoteFieldValueRepository.upsertFromServer(localData); - return { id: localFieldValue.id, resolution }; + return { id: localFieldValue.id, resolution: "server_wins" }; } /** @@ -900,18 +847,14 @@ export class ConflictResolver { } /** - * Create a conflict resolver with the given options + * Create a conflict resolver */ -export function createConflictResolver( - options: ConflictResolverOptions = {}, -): ConflictResolver { - return new ConflictResolver(options); +export function createConflictResolver(): ConflictResolver { + return new ConflictResolver(); } /** - * Default conflict resolver using CRDT (Automerge) strategy + * Default conflict resolver using CRDT (Automerge) merge * Falls back to server_wins when CRDT data is unavailable */ -export const conflictResolver = new ConflictResolver({ - strategy: "crdt", -}); +export const conflictResolver = new ConflictResolver(); |
