From 83681d6236ce67b032ba1192c151b27e566b30a0 Mon Sep 17 00:00:00 2001 From: David Brochart Date: Fri, 23 Feb 2024 16:03:16 +0100 Subject: [PATCH] Support document forking --- javascript/src/api.ts | 44 ++++++++++++++++++++++++++++++++++++- javascript/src/ydocument.ts | 27 ++++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/javascript/src/api.ts b/javascript/src/api.ts index 807fd18..bd67a95 100644 --- a/javascript/src/api.ts +++ b/javascript/src/api.ts @@ -20,7 +20,7 @@ import type { JSONValue, PartialJSONValue } from '@lumino/coreutils'; -import type { IObservableDisposable } from '@lumino/disposable'; +import type { IDisposable, IObservableDisposable } from '@lumino/disposable'; import type { ISignal } from '@lumino/signaling'; /** @@ -80,6 +80,26 @@ export interface ISharedBase extends IObservableDisposable { transact(f: () => void, undoable?: boolean): void; } +/** + * An interface for a document provider. + */ +export interface IDocumentProvider extends IDisposable { + /** + * Returns a Promise that resolves when the document provider is ready. + */ + readonly ready: Promise; + + /** + * Request to fork the room in the backend, and connect the shared document to the forked room. + */ + fork(): Promise; + + /** + * Connect a shared document to a forked room with forkId and return a document provider. + */ + connectFork(forkId: string, sharedDocument: ISharedDocument): IDocumentProvider; +} + /** * Implement an API for Context information on the shared information. * This is used by, for example, docregistry to share the file-path of the edited content. @@ -114,6 +134,28 @@ export interface ISharedDocument extends ISharedBase { * The changed signal. */ readonly changed: ISignal; + + /** + * Get a provider with a given ID + * + * @param providerId The provider ID + */ + getProvider(providerId: string, sharedModel?: ISharedDocument): IDocumentProvider; + + /** + * Set a provider for this document + * + * @param providerId Provider ID (either 'root' or a UUID) + * @param provider The document provider + */ + setProvider(providerId: string, provider: IDocumentProvider): void; + + /** + * Add a fork ID to the document's ystate, as a new key 'fork_{forkId}' + * + * @param forkId The fork ID to add to the document + */ + addFork(forkId: string): void; } /** diff --git a/javascript/src/ydocument.ts b/javascript/src/ydocument.ts index c923c12..90e1114 100644 --- a/javascript/src/ydocument.ts +++ b/javascript/src/ydocument.ts @@ -7,7 +7,7 @@ import { JSONExt, JSONObject, JSONValue } from '@lumino/coreutils'; import { ISignal, Signal } from '@lumino/signaling'; import { Awareness } from 'y-protocols/awareness'; import * as Y from 'yjs'; -import type { DocumentChange, ISharedDocument, StateChange } from './api.js'; +import type { DocumentChange, IDocumentProvider, ISharedDocument, StateChange } from './api'; /** * Generic shareable document. @@ -28,6 +28,8 @@ export abstract class YDocument this._awareness = new Awareness(this._ydoc); this._ystate.observe(this.onStateChanged); + + this._providers = {}; } /** @@ -35,6 +37,28 @@ export abstract class YDocument */ abstract readonly version: string; + addFork(forkId: string) { + this.ystate.set(`fork_${forkId}`, 'new'); + } + + getProvider(providerId: string, sharedModel?: ISharedDocument): IDocumentProvider { + if (!(providerId in this._providers)) { + if (providerId === 'root') { + throw new Error('Cannot get a new provider for root document'); + } + if (sharedModel === undefined) { + throw new Error('New provider needs a shared document'); + } + const root_provider = this._providers['root']; + this._providers[providerId] = root_provider.connectFork(providerId, sharedModel!); + } + return this._providers[providerId]; + } + + setProvider(providerId: string, provider: IDocumentProvider) { + this._providers[providerId] = provider; + } + /** * YJS document. */ @@ -200,6 +224,7 @@ export abstract class YDocument private _awareness: Awareness; private _isDisposed = false; private _disposed = new Signal(this); + private _providers: { [key: string]: IDocumentProvider }; } /**