The protocol collab plugin [solved]

Hello Community.

I have a hard time getting the collab plugin to work. I’ve read the documentation, set up a blank project, and had a detailed at the source code as well, but still face a problem. I use vite as a building tool and got the webpage demo to work.

The problem I’m facing at the moment, the clients do not apply the received changes.
The collaborative plugin looks like this. In the project, I use socket.io, Vite, and Vue 3.

import { ViewPlugin } from "@codemirror/view";
import { Update, collab, sendableUpdates, receiveUpdates, getSyncedVersion } from "@codemirror/collab";

function peerExtension(startVersion) {
    let plugin = ViewPlugin.fromClass(
        class {
            constructor(view) {
                this.pushing = false;
                this.done = false;
                this.view = view;
                this.pull();
            }

            update(update) {
                if (update.docChanged) this.push();
            }

            async push() {
                await $connection();

                let fullUpdates = sendableUpdates(this.view.state);

                let updates = fullUpdates.map((u) => ({
                    clientID: u.clientID,
                    changes: u.changes.toJSON(),
                }));

                if (this.pushing || !updates.length) return console.log("maybe next time ...");
                this.pushing = true;

                let version = getSyncedVersion(this.view.state);
                console.log("@push", version, updates);

                $socket.emit("collab", pad.value, "pushUpdates", { version, updates }, (data) => {
                    console.log("$push", data);
                    this.pushing = false;
                    if (sendableUpdates(this.view.state).length) setTimeout(() => this.push(), 100);
                });
            }

            async pull() {
                await $connection();
                $socket.on("collab-pull", (path) => {
                    if (pad.value !== path) return;
                    let version = getSyncedVersion(this.view.state);

                    console.log("@pull", pad.value, version);

                    $socket.emit("collab", pad.value, "pullUpdates", { version }, (data) => {
                        const updates = data;

                        console.log("$pull updates", updates);

                        const receive = receiveUpdates(this.view.state, updates);
                        console.log("$pull recieve.changes", receive.changes);

                        this.view.dispatch(receive);

                        console.log("->", this.view.state.doc.toString());

                    });
                });

            }

            destroy() {
                this.done = true;
            }
        }
    );
    return [collab({ startVersion }), plugin];
}

The authority is based on the documentation as well. Once a collab socket.io message is emitted, all clients receive the collab-pull message, with the data in the callback.

Now when testing with two clients, at the beginning, the initial “Start document” text is present on both clients, let’s call them X and Y. Then on client X, a character is typed, a small ‘x’ for example.

This is the message pushed to the authority, …

[ {
        "clientID": "2y4z0m",
        "changes": [ 14, [ 0, "x" ] ] 
} ]

… and is the received as it is, at socket collab pullUpdates, logged at “$pull updates” correctly.

Applying the receiveUpdates on X seems to be correct as far as I can tell.
recieve.changes has inserted [] and sections [15, -1].
… looking at the receiveUpdates implementation, own is 1, updates is an empty array.
So far so good.

However Y seems to handle the same message incorrectly. Looking at the implementation of receiveUpdates, the variable own is 0 and changes has the value of the array [ 14, [ 0, 'x' ] ]
The variable called changes has that same value.
The returned changes from receiveUpdates is {"sections": [ 14,-1], "inserted": []} at the first and at every subsequent update.
So doc does not change, not in that state, and not in the view, and the change is not reflected.

Does anyone have any idea why the received updates do not apply correctly?

Baah. Found my mistake.

$socket.emit("collab", pad.value, "pullUpdates", { version }, (data) => {
                        const updates = data.map((u) => ({
                            changes: ChangeSet.fromJSON(u.changes),
                            clientID: u.clientID,
                        }));

                        console.log("$pull updates", updates);

                        const receive = receiveUpdates(this.view.state, updates);
                        console.log("$pull recieve.changes", receive.changes);

                        this.view.dispatch(receive);

                        console.log("->", this.view.state.doc.toString());
                    });

Ok. … so this is the correct one.