CRDT-inspired adaptation of prosemirror-collab

Hey @TeemuKoivisto ,

So these IDs are client-generated for each step? One particular problem with ProseMirror docs is the lack of global IDs for nodes which many resolve to add manually for linking purposes and just to prevent constant remapping & iterating of the doc. This in theory maps nicely with CRDTs as the blocks have unique IDs but afaik they are not used at least by default libraries. But it’d be nice if this was easily achievable since IDs are so commonly used.

Yes, a client assigns an immutable ID for each ProseMirror position that is “created” by a local ReplaceStep or ReplaceAroundStep. You can use the ID corresponding to a node’s starting position as a global “node ID” and look up the corresponding node in any future state.

Caveat: Currently, if you replace a node with one of a different type (e.g., converting a <p> into an <h1>), the demo models that as a delete-then-insert. So the replaced node gets a new, different ID. This should be fixable.

I suppose access control works normally with these enhanced collabs? You just look at the changed range and revert/discard the step if unauthorized?

Yes, the server has complete control over the log and can read/reject/add AnnotatedSteps at will. Concurrent client updates should still “make sense” even though this changes the state to which they’re applied. (In theory - I have not yet tested this yet.)

One benefit of CRDTs is the added bonus of optimized implementations which probably are an order of magnitude faster than any NodeJS server can do.

In experiments on a predecessor of list-positions (Collabs), we were able to shove 100+ active users into a rich-text document without overwhelming a simple NodeJS server running on an AWS t2.medium. The main bottleneck was client-side Quill rendering; I ought to try again with list-positions + ProseMirror and see what happens.

2 Likes