@jodit/collab-protocol
v0.1.7
Published
Wire protocol for Jodit collaborative editing: id-addressed patches, OT transform, sequencer/client-sync reference implementation, auth/identity contract. Pure TypeScript — no DOM, no Jodit.
Maintainers
Readme
@jodit/collab-protocol
The wire protocol for Jodit collaborative editing — shared by the editor
plugin (@jodit/collab) and any backend implementation. Pure TypeScript: no
DOM, no Jodit, no IO. This package IS the spec: implement your own server
against these types, schemas and transform functions, or reuse the reference
DocumentSession / ClientSync directly.
What's inside
| Module | Purpose |
| ------------- | -------------------------------------------------------------------------- |
| patch.ts | Wire format: id-addressed insert / remove / text(splice) / attr patches |
| doc.ts | Pure JSON document model: applyPatch, invertPatch, tolerance rules |
| text-op.ts | Character-level OT for one text node (retain/delete/insert) |
| xform.ts | xform/xformBatch: transform concurrent batches; convergence guaranteed |
| session.ts | Reference sequencer (DocumentSession) + client rebase (ClientSync) |
| identity.ts | Auth contract: resolveIdentity, roles, guest generator |
| messages.ts | Zod schemas for every WS message (client schemas are strict) |
Concurrency model in one paragraph
Patches address nodes by stable ids, so structural edits commute; the only
index in the format is the character offset inside a single text node, which
is transformed with classic text OT. The server is the authoritative
sequencer: a submit {opId, baseSeq, patches} is rebased against everything
the author had not seen, gets the next seq, and is broadcast to everyone —
including the author, who treats it as the ack. Clients rebase incoming
batches over their pending (unacknowledged) edits with the same xform
functions the server uses. Convergence — apply(apply(d,B),A') ===
apply(apply(d,A),B') — is enforced by property-based tests (fast-check) and
a randomized client/server fuzz harness.
Identity: always server-assigned
The client's hello carries a token and nothing else — the schemas are
strict, so a frontend sending name/color is a validation error, not a
suggestion. Who you are comes back in welcome.self; who your peers are
comes in welcome.peers / peer.join / server-enriched presence.
import { resolveIdentity, type AuthOptions } from '@jodit/collab-protocol';
// Production: plug in your own token check (JWT, session store, API key…)
const auth: AuthOptions = {
checkAuthentication: async (token, ctx) => {
const user = await verifyJwt(token);
return user
? { userId: user.id, name: user.displayName, color: user.color, role: user.role }
: null; // invalid token → connection rejected ('auth_failed')
},
authorize: (identity, docId, action) => acl.check(identity.userId, docId, action),
};
// Demo mode: no tokens, every connection gets a generated identity
// ("Guest Amber Fox", stable per seed) — names never come from the frontend.
const demoAuth: AuthOptions = { allowAnonymous: true, guestRole: 'writer' };Flow (resolveIdentity implements it, use it on the WS upgrade):
token present → checkAuthentication → identity | 'auth_failed'
token absent → allowAnonymous ? guest identity : 'auth_required'Note: an invalid token is rejected even in anonymous mode — silent downgrade to guest would mask broken tokens.
Reference sync loop
import { DocumentSession, ClientSync } from '@jodit/collab-protocol';
// server
const session = new DocumentSession(initialDoc);
const entry = session.submit(clientId, msg.opId, msg.baseSeq, msg.patches);
broadcastToRoom(entry); // to everyone, author included (= ack)
// client
const sync = new ClientSync(clientId, initialDoc);
const submit = sync.local(patchesFromEditor); // → send if not null
const { apply, submit: next } = sync.receive(broadcast);
editor.collab.applyRemote(apply); // rebased against local pending edits
if (next) send(next);Scripts
npm test # unit, property-based convergence, session fuzz, OT window
npm run build # dist ESM+CJS+d.ts (tsup)License
Commercial — see LICENSE.md. Free to evaluate; a paid license
is required for production use. Read more at https://xdsoft.net/jodit/pro/.
