Yjs collaboration
Bind Coaction-owned pure state to a Y.Doc and let a Yjs provider handle peer transport.
@coaction/yjs synchronizes a Coaction store's pure data with Yjs. It does not provide a network
connection; choose y-websocket, y-webrtc, or another Yjs provider for peer transport.
Middleware setup
import { create } from 'coaction';
import { yjs } from '@coaction/yjs';
const store = create(source, {
middlewares: [yjs()]
});The middleware creates a Y.Doc, binds the store, and connects cleanup to store.destroy().
Use an existing Y.Doc
import { bindYjs } from '@coaction/yjs';
import { Doc } from 'yjs';
import { WebsocketProvider } from 'y-websocket';
const doc = new Doc();
const provider = new WebsocketProvider(
'wss://collaboration.example',
'room-id',
doc
);
const binding = bindYjs(store, {
doc,
key: 'counter'
});
// later
binding.destroy();
provider.destroy();
store.destroy();bindYjs() returns the doc, root map, syncNow(), and destroy().
Storage and sync model
- State lives under
doc.getMap(key).get('state'). - Nested objects and arrays become nested
Y.MapandY.Arraystructures. - Local Coaction changes are diffed against the last synchronized snapshot.
- Remote Yjs updates are replayed into Coaction.
- Methods and computed getters are not part of the synchronized payload.
The default key is coaction:${store.name}.
Conflict semantics
Concurrent edits to different fields can merge through Yjs. Conflicts on the same scalar field follow Yjs semantics, effectively last-writer-wins for that value. When an operation must be commutative—such as a distributed counter—model that field with a CRDT-native Yjs structure or operation instead of repeatedly replacing a scalar.
Requirements and limits
- Keep synchronized state plain and serializable.
- Yjs binding is supported on local and shared-main stores, not shared clients.
- Bind the owning store only; a client mirror must not create a second collaboration authority.
- Very large or highly volatile trees can generate substantial update traffic.
- Always release old bindings to prevent duplicate observers and stale writes.
Remote snapshots and deletes still follow Coaction's fixed schema. Unknown root keys are rejected;
known single-store fields may become undefined if absent from an exact replacement.