- Move api.server.ts, errors.ts, types.ts, utils.ts, relativeTime.ts to lib/shared/ - Move person relationship components to lib/person/relationship/ - Move Stammbaum components to lib/person/genealogy/ - Move HelpPopover to lib/shared/primitives/ - Update all import paths across routes, specs, and lib files - Update vi.mock() paths in server-project test files - Remove now-empty legacy directories (components/, hooks/, server/, etc.) - Update vite.config.ts coverage include paths for new structure - Update frontend/CLAUDE.md to reflect domain-based lib/ layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import type { PersonMention, TranscriptionBlockData } from '$lib/shared/types';
|
|
|
|
/**
|
|
* Sentinel thrown by saveBlockWithConflictRetry after a 409 rename-mid-edit
|
|
* has been merged into local state. Surfaces to the autosave hook as an
|
|
* error (so the UI shows the retry indicator), but distinguishable from a
|
|
* genuine network failure via the code. Carries the merged block snapshot
|
|
* on its `merged` property so the caller can update local state without
|
|
* a second roundtrip.
|
|
*/
|
|
export class BlockConflictResolvedError extends Error {
|
|
readonly code = 'CONFLICT_RESOLVED' as const;
|
|
merged?: TranscriptionBlockData;
|
|
constructor(blockId: string) {
|
|
super(
|
|
`Block ${blockId} was rebased onto the latest server snapshot — retry to save the merged result`
|
|
);
|
|
this.name = 'BlockConflictResolvedError';
|
|
}
|
|
}
|
|
|
|
type MergeArgs = {
|
|
serverBlock: TranscriptionBlockData;
|
|
localText: string;
|
|
localMentions: PersonMention[];
|
|
};
|
|
|
|
/**
|
|
* Resolves a 409-Conflict from the server by combining the latest server
|
|
* snapshot with the transcriber's unsaved local edits (B12b).
|
|
*
|
|
* Rules:
|
|
* - The transcriber's typed text always wins — never overwrite their input.
|
|
* - Server is the source of truth for the displayName of any person it
|
|
* knows about; renames that just landed on the server replace stale local
|
|
* names by personId.
|
|
* - Local-only mentions added since the last save are preserved.
|
|
* - All non-mention fields (version, sortOrder, reviewed, updatedAt, ...)
|
|
* come from the server snapshot so the next save sends the current
|
|
* revision and matches the latest persisted state.
|
|
*/
|
|
export function mergeBlockOnConflict(args: MergeArgs): TranscriptionBlockData {
|
|
const serverIds = new Set(args.serverBlock.mentionedPersons.map((m) => m.personId));
|
|
const localOnly = args.localMentions.filter((m) => !serverIds.has(m.personId));
|
|
return {
|
|
...args.serverBlock,
|
|
text: args.localText,
|
|
mentionedPersons: [...args.serverBlock.mentionedPersons, ...localOnly]
|
|
};
|
|
}
|