How to inspect a Y.Doc update

doc.on('update',...) can be used to detect changes within a given Y.Doc.

Is it possible to find out (from this update event) which “shared type” has been changed (and, perhaps, in what way)? And, if yes, how?

Or will I always have to observe every “shared type” individually?

It may be possible to extract the shared types from transaction.change, provided as the fourth parameter to the update event handler.

You might also consider observeDeep on a root Y.Map.

oh, there is a fourth parameter? good to know, thanks!

The secret fourth parameter. Advanced users only! :wink:

Damn…I’m still a Yjs newbie

Oh, I misunderstood - I know the transaction parameter - and already inspected its changed property, but that structure seems to lack important information (such as the name of the top-level Y.Doc entry that was changed…

[Edit] the key of a transaction.changed entry seems to be the changed structure itself?

I’m not sure. Usually I do shared-type specific behavior in the observe or deepObserve events, not the update event.

by the way: I am now able to inspect updates to top-level Y.Maps, but not to top-level Y.Arrays or Y.Texts

observe and deepObserve work on Y.Doc entries only, not on Y.Docs themselves.

For scalability reasons, I would prefer a single handler for the main Y.Doc rather than many handlers for every single Y.Doc entry or SubDoc…

If you want to see some code:

  const sharedDoc = new Y.Doc()
    sharedDoc.on('update',(Update,Origin,Doc,Transaction) => {
      console.log('new transaction')
      Transaction.changed.forEach((ChangeSet,Container) => {
        if (
          (Container instanceof Y.Map) ||
          (Container._map instanceof Map) && (Container._map.size > 0)
        ) {
          console.log('- Y.Map:')
          ChangeSet.forEach((Value,Key) => {
            console.log('  -',Key,'=',Value)
          })
          return
        }

        if (
          (Container instanceof Y.Array) ||
          (Container._start != null) && ('arr' in Container._start.content)
        ) {
          console.log('- Y.Array:',Transaction)
          return
        }

        if (
          (Container instanceof Y.Text) ||
          (Container._start != null) && ('str' in Container._start.content)
        ) {
          console.log('- Y.Text:',Transaction)
          return
        }

        if (
          (Container instanceof Y.XmlFragment) ||
          (Container._start != null) && ('type' in Container._start.content)
        ) {
          console.log('- Y.XmlFragment:',Transaction)
          return
        }

        console.log('Transaction',Transaction)
      })
    })

Those strange type detection expressions are needed since updates from a provider seem to look different than updates from business logic…(sigh)

If you have a single root Map, it’s only a one observeDeep for all shared types.

transaction.origin might help you differentiate transactions originating on a local doc (which is usually set to doc.clientID) and transactions synced over a provider (which is usually set to the provider instance).

Indeed,

while Y.Doc look a bit like (top-level) maps with an additional sub-doc registry, it might be better to always add a first indirection in form of a top-level Y.Map or Y.Array which could then be (deeply) observed more easily…

Yes. Unfortunately subdoc support is severely lacking. But nested shared types work well across providers.