Files
Michael Ramos 195328f7a6 Persist saved annotate file edits in drafts (#936)
* Persist saved annotate file edits in drafts

* Handle stale saved file edit context

* Test source edit conflict actions

* Return source metadata for single-file docs

* Fix saved file edit conflict races

* Handle deleted source files in annotate edits

* Tighten annotate missing-file recovery

* Reset edit state for missing file reopen

* Tighten source edit restore path handling

* Harden source edit recovery paths

* Harden source edit disk reconciliation

* Fix live file tree startup delay

* Document file tree watcher startup ordering

* Preserve source save through missing files and symlinks

* Harden missing source file recovery

* Tighten annotate source edit boundaries
2026-06-18 21:31:25 -07:00

48 lines
1.5 KiB
TypeScript

import type { SavedFileChangeDraftData } from './editableDocuments';
import type { SourceSaveProbeResult } from './sourceDocumentClient';
export interface SavedFileChangeValidationResult {
valid: SavedFileChangeDraftData[];
dropped: Array<{ change: SavedFileChangeDraftData; reason: 'changed' | 'missing' | 'noop' }>;
unverified: SavedFileChangeDraftData[];
}
export async function validateSavedFileChanges(
changes: SavedFileChangeDraftData[],
resolveSourceSave: (change: SavedFileChangeDraftData) => Promise<SourceSaveProbeResult>,
): Promise<SavedFileChangeValidationResult> {
const valid: SavedFileChangeDraftData[] = [];
const dropped: SavedFileChangeValidationResult['dropped'] = [];
const unverified: SavedFileChangeDraftData[] = [];
for (const change of changes) {
if (change.beforeText === change.afterText) {
dropped.push({ change, reason: 'noop' });
continue;
}
const expectedHash = change.afterHash ?? change.sourceSave.hash;
const probe = await resolveSourceSave(change);
if (probe.status === 'unavailable') {
unverified.push(change);
continue;
}
if (probe.status === 'missing') {
dropped.push({ change, reason: 'missing' });
continue;
}
if (probe.sourceSave.hash !== expectedHash) {
dropped.push({ change, reason: 'changed' });
continue;
}
valid.push({
...change,
sourceSave: probe.sourceSave,
afterHash: probe.sourceSave.hash,
});
}
return { valid, dropped, unverified };
}