fix(git-sync): merge git body into the live doc block-by-block (no clobber)
Supersedes the active-session "defer" guard with a real merge (review #5 — "запись делать через мерж", not skip-while-editing). writeBody no longer does delete-all + re-insert (which discarded a concurrent editor's in-flight changes on every sync). It now diffs the live body against the incoming git body at TOP-LEVEL BLOCK granularity (LCS over a canonical structural serialization) and applies only the minimal inserts/deletes: - a block a human is editing is left UNTOUCHED when git changed a DIFFERENT block; - an unchanged resync is a complete 0-op write; - Yjs CRDT-merges the minimal ops with concurrent edits. New yjs-body-merge.ts (mergeXmlFragments + cloneXmlNode + diffBlocks) is pure-Yjs and unit-tested with real Y.Docs (8 tests): identical->0 ops, edit-one-block keeps the other block instances, append/delete keep neighbours, marks survive the cross-doc clone. Crash-safety kept: the incoming doc is built before the connection opens, so a transform failure can't empty the body. Removed: the ActiveEditSessionError defer path and the now-unused CollaborationGateway.getActiveEditorCount. Honest limitation: this is a 2-way merge — for a block BOTH sides changed since the last sync, git wins (no common ancestor to decide). A full 3-way merge would need the last-synced base plumbed from the engine; the dominant cases (unchanged resync, edits to different blocks) are now lossless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -137,21 +137,6 @@ export class CollaborationGateway {
|
||||
return this.hocuspocus.getDocumentsCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of LIVE human editor sessions (websocket connections) currently open
|
||||
* on a document, or 0 if the document is not loaded. Unlike
|
||||
* `Document.getConnectionsCount()` this deliberately excludes server-side
|
||||
* direct connections (`directConnectionsCount`, e.g. the git-sync writer
|
||||
* itself), so callers can tell whether a real person is editing right now.
|
||||
*
|
||||
* NOTE: this reflects only THIS instance. In a Redis-clustered deployment an
|
||||
* editor attached to another node is not counted; for the single-instance
|
||||
* deployments this guards (git-sync) that is exactly the live set.
|
||||
*/
|
||||
getActiveEditorCount(documentName: string): number {
|
||||
return this.hocuspocus.documents.get(documentName)?.connections.size ?? 0;
|
||||
}
|
||||
|
||||
handleYjsEvent<TName extends keyof CollabEventHandlers>(
|
||||
eventName: TName,
|
||||
documentName: string,
|
||||
|
||||
Reference in New Issue
Block a user