const comment = await db.comments.get(commentId);
try {
DexieYProvider.load(comment.contentDoc);
await comment.contentDoc.whenLoaded; // waits until loaded.
// At this point the doc is loaded, observed and you
// can invoke anything from the Y.js ecosystem, such
// as a Tiptap or Prosemirror editor.
} finally {
DexieYProvider.release(comment.contentDoc);
}
When using these options, every object retrieved from table containing :Y specifications, will contain an instance of a Y.Doc no matter if that property has been created or not. The property is neither enumerable nor own so JSON.stringify(obj) won't reveleal it. Accessing the Y.Doc will retrieve an empty, unloaded Y.Doc instance.
Y properties are never nullish. If declared in the dexie schema, they'll exist on all objects from all queries (toArray(), get() etc).
Y properties are not own properties. They are set on the prototype of the returned object.
Y properties are readonly, except when adding new objects to a table (using table.add() or bulkAdd())
If providing a custom Y.Doc to add() or bulkAdd() its udates will be cloned when added.
If not providing the Y.Doc or setting the Y property to null when adding objects, there will still be an Y.Doc property on query results of the object, since Y props are defined by the schema and cannot be null or undefined.
Y properties on dexie objects are readonly. You can not replace them with another document or update them using table.update() or collection.modify(). The only way to update Y.Docs is via the Y.js library methods on the document instance.
Y properties are not loaded until using DexieYProvider.load() or the new react hook useDocument()
Y.Doc instances are kept in a global cache integrated with FinalizationRegistry. First time you access the getter, it will be created, and will stay in cache until it's garbage collected. This means that you'll always get the same Y.Doc instance when querying the same Y property of a the same object. This holds true even if the there are multiple obj instances representing the same ID in the database. All of these will hold one single instance of the Y.Doc because the cache is connected to the primary key of the parent object.
How it works
Internally, every declared Y property generates a dedicated table for Y.js updates tied to the parent table and the property name. Whenever a document is updated, a new entry in this table is added.
DexieYProvider is responsible of loading and observing updates in both directions.
Integrations
Y.js allows multiple providers on the same document. It is possible to combine DexieYProvider with other providers, but it is also possible for dexie addons to extend the provider behavior - such as adding awareness and sync.
dexie-cloud-addon@4.1.0-beta.44
This is the next verison of dexie-cloud-addon that extends the behavior of DexieYProvider to also support awareness and sync over websockets with Dexie Cloud Server.
Bugfixes
No double initial sync
Earlier versions of dexie-cloud-addon performed two initial sync phases also when requireAuth was specified. This is now optimized to only be done in a single authenticated initial sync.
Better migration from vanilla dexie to dexie-cloud
A vanilla Dexie app with multiple versions and upgraders could not make it to the cloud earlier because we previously disallowed using upgraders between versions when using dexie-cloud-addon. With this version, we allow that but make sure that the change tracking and sync is disabled during upgrade transactions. This allows vanilla dexie apps for easier migrating to cloud by allowing them to run the upgraders needed and then activate dexie-cloud and sync.
New Example App
The Dexie Cloud Starter is a new nextjs app that make full use of online text editing, full-text search and dexie cloud authentication, Github SSO and data sharing in realms.
dexie-react-hooks@4.1.0-beta.43
New hook useDocument() makes use of DexieYProvider as a hook rather than loading and releasing imperatively.
function MyComponent(commentId: string) {
// Query comment object:
const comment = useLiveQuery(() => db.comments.get(comentId));
// Use it's document property (might be nullish):
const provider = useDocument(comment?.contentDoc);
// Pass provider and document to some Y.js compliant code in the ecosystem of such (unless nullish)...
return provider
? <CommentEditor doc={comment.contentDoc} provider={provider} />
: null;
}