Jelajahi Sumber

Add a plugin to be able to swap the doc provider (#10256)

* Add plugin to create an IProviderFactory

* Fix typo in tsconfig.json

* Fix plugin id

* Lint

* Rename IProvider to IDocumentProvider

* Read PageConfig collaborative from the plugin

* Integrity

* Provider URL is specific to websocket

* Consistent WebSocket spelling

* Add docprovider-extension package

* Remove schemaDir

* Remove TODO

* Update sideEffects

* Update plugin id

* Add docprovider-extension to SKIP_CSS

* Add docstrings

* More docstrings
Jeremy Tuloup 3 tahun lalu
induk
melakukan
4bd902c53d

+ 1 - 0
buildutils/src/ensure-repo.ts

@@ -158,6 +158,7 @@ const SKIP_CSS: Dict<string[]> = {
     '@jupyterlab/debugger',
     '@jupyterlab/debugger-extension',
     '@jupyterlab/docmanager-extension',
+    '@jupyterlab/docprovider-extension',
     '@jupyterlab/documentsearch-extension',
     '@jupyterlab/extensionmanager',
     '@jupyterlab/extensionmanager-extension',

+ 4 - 0
dev_mode/package.json

@@ -38,6 +38,7 @@
     "@jupyterlab/docmanager": "~3.1.0-alpha.8",
     "@jupyterlab/docmanager-extension": "~3.1.0-alpha.8",
     "@jupyterlab/docprovider": "~3.1.0-alpha.8",
+    "@jupyterlab/docprovider-extension": "~3.1.0-alpha.8",
     "@jupyterlab/docregistry": "~3.1.0-alpha.8",
     "@jupyterlab/documentsearch": "~3.1.0-alpha.8",
     "@jupyterlab/documentsearch-extension": "~3.1.0-alpha.8",
@@ -132,6 +133,7 @@
     "@jupyterlab/csvviewer-extension": "~3.1.0-alpha.8",
     "@jupyterlab/debugger-extension": "~3.1.0-alpha.8",
     "@jupyterlab/docmanager-extension": "~3.1.0-alpha.8",
+    "@jupyterlab/docprovider-extension": "~3.1.0-alpha.8",
     "@jupyterlab/documentsearch-extension": "~3.1.0-alpha.8",
     "@jupyterlab/extensionmanager-extension": "~3.1.0-alpha.8",
     "@jupyterlab/filebrowser-extension": "~3.1.0-alpha.8",
@@ -211,6 +213,7 @@
       "@jupyterlab/csvviewer-extension": "",
       "@jupyterlab/debugger-extension": "",
       "@jupyterlab/docmanager-extension": "",
+      "@jupyterlab/docprovider-extension": "",
       "@jupyterlab/documentsearch-extension": "",
       "@jupyterlab/extensionmanager-extension": "",
       "@jupyterlab/filebrowser-extension": "",
@@ -322,6 +325,7 @@
       "@jupyterlab/docmanager": "../packages/docmanager",
       "@jupyterlab/docmanager-extension": "../packages/docmanager-extension",
       "@jupyterlab/docprovider": "../packages/docprovider",
+      "@jupyterlab/docprovider-extension": "../packages/docprovider-extension",
       "@jupyterlab/docregistry": "../packages/docregistry",
       "@jupyterlab/documentsearch": "../packages/documentsearch",
       "@jupyterlab/documentsearch-extension": "../packages/documentsearch-extension",

+ 1 - 0
dev_mode/style.js

@@ -10,6 +10,7 @@ import '@jupyterlab/console-extension/style/index.js';
 import '@jupyterlab/csvviewer-extension/style/index.js';
 import '@jupyterlab/debugger-extension/style/index.js';
 import '@jupyterlab/docmanager-extension/style/index.js';
+import '@jupyterlab/docprovider-extension/style/index.js';
 import '@jupyterlab/documentsearch-extension/style/index.js';
 import '@jupyterlab/extensionmanager-extension/style/index.js';
 import '@jupyterlab/filebrowser-extension/style/index.js';

+ 1 - 0
packages/docmanager-extension/package.json

@@ -42,6 +42,7 @@
     "@jupyterlab/apputils": "^3.1.0-alpha.8",
     "@jupyterlab/coreutils": "^5.1.0-alpha.8",
     "@jupyterlab/docmanager": "^3.1.0-alpha.8",
+    "@jupyterlab/docprovider": "^3.1.0-alpha.8",
     "@jupyterlab/docregistry": "^3.1.0-alpha.8",
     "@jupyterlab/mainmenu": "^3.1.0-alpha.8",
     "@jupyterlab/services": "^6.1.0-alpha.8",

+ 20 - 10
packages/docmanager-extension/src/index.ts

@@ -30,6 +30,8 @@ import {
   SavingStatus
 } from '@jupyterlab/docmanager';
 
+import { IDocumentProviderFactory } from '@jupyterlab/docprovider';
+
 import { DocumentRegistry } from '@jupyterlab/docregistry';
 
 import { IMainMenu } from '@jupyterlab/mainmenu';
@@ -85,13 +87,16 @@ namespace CommandIDs {
   export const showInFileBrowser = 'docmanager:show-in-file-browser';
 }
 
-const pluginId = '@jupyterlab/docmanager-extension:plugin';
+/**
+ * The id of the document manager plugin.
+ */
+const docManagerPluginId = '@jupyterlab/docmanager-extension:plugin';
 
 /**
  * The default document manager provider.
  */
 const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
-  id: pluginId,
+  id: docManagerPluginId,
   provides: IDocumentManager,
   requires: [ISettingRegistry, ITranslator],
   optional: [
@@ -99,7 +104,8 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
     ICommandPalette,
     ILabShell,
     IMainMenu,
-    ISessionContextDialogs
+    ISessionContextDialogs,
+    IDocumentProviderFactory
   ],
   activate: (
     app: JupyterFrontEnd,
@@ -109,7 +115,8 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
     palette: ICommandPalette | null,
     labShell: ILabShell | null,
     mainMenu: IMainMenu | null,
-    sessionDialogs: ISessionContextDialogs | null
+    sessionDialogs: ISessionContextDialogs | null,
+    docProviderFactory: IDocumentProviderFactory | null
   ): IDocumentManager => {
     const trans = translator.load('jupyterlab');
     const manager = app.serviceManager;
@@ -148,7 +155,8 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
       setBusy: (status && (() => status.setBusy())) ?? undefined,
       sessionDialogs: sessionDialogs || undefined,
       translator,
-      collaborative: true
+      collaborative: true,
+      docProviderFactory: docProviderFactory ?? undefined
     });
 
     // Register the file operations commands.
@@ -209,7 +217,7 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
     };
 
     // Fetch the initial state of the settings.
-    Promise.all([settingRegistry.load(pluginId), app.restored])
+    Promise.all([settingRegistry.load(docManagerPluginId), app.restored])
       .then(([settings]) => {
         settings.changed.connect(onSettingsUpdated);
         onSettingsUpdated(settings);
@@ -222,7 +230,7 @@ const docManagerPlugin: JupyterFrontEndPlugin<IDocumentManager> = {
     // allowing us to dynamically populate a help string with the
     // available document viewers and file types for the default
     // viewer overrides.
-    settingRegistry.transform(pluginId, {
+    settingRegistry.transform(docManagerPluginId, {
       fetch: plugin => {
         // Get the available file types.
         const fileTypes = toArray(registry.fileTypes())
@@ -260,7 +268,7 @@ Available file types:
 
     // If the document registry gains or loses a factory or file type,
     // regenerate the settings description with the available options.
-    registry.changed.connect(() => settingRegistry.reload(pluginId));
+    registry.changed.connect(() => settingRegistry.reload(docManagerPluginId));
 
     return docManager;
   }
@@ -729,9 +737,11 @@ function addCommands(
       const value = !docManager.autosave;
       const key = 'autosave';
       return settingRegistry
-        .set(pluginId, key, value)
+        .set(docManagerPluginId, key, value)
         .catch((reason: Error) => {
-          console.error(`Failed to set ${pluginId}:${key} - ${reason.message}`);
+          console.error(
+            `Failed to set ${docManagerPluginId}:${key} - ${reason.message}`
+          );
         });
     }
   });

+ 3 - 0
packages/docmanager-extension/tsconfig.json

@@ -18,6 +18,9 @@
     {
       "path": "../docmanager"
     },
+    {
+      "path": "../docprovider"
+    },
     {
       "path": "../docregistry"
     },

+ 1 - 0
packages/docmanager/package.json

@@ -44,6 +44,7 @@
   "dependencies": {
     "@jupyterlab/apputils": "^3.1.0-alpha.8",
     "@jupyterlab/coreutils": "^5.1.0-alpha.8",
+    "@jupyterlab/docprovider": "^3.1.0-alpha.8",
     "@jupyterlab/docregistry": "^3.1.0-alpha.8",
     "@jupyterlab/services": "^6.1.0-alpha.8",
     "@jupyterlab/statusbar": "^3.1.0-alpha.8",

+ 14 - 3
packages/docmanager/src/manager.ts

@@ -5,20 +5,22 @@ import { ISessionContext, sessionContextDialogs } from '@jupyterlab/apputils';
 
 import { PathExt } from '@jupyterlab/coreutils';
 
-import { UUID } from '@lumino/coreutils';
-
 import {
   DocumentRegistry,
   Context,
   IDocumentWidget
 } from '@jupyterlab/docregistry';
 
+import { IDocumentProviderFactory } from '@jupyterlab/docprovider';
+
 import { Contents, Kernel, ServiceManager } from '@jupyterlab/services';
 
 import { nullTranslator, ITranslator } from '@jupyterlab/translation';
 
 import { ArrayExt, find } from '@lumino/algorithm';
 
+import { UUID } from '@lumino/coreutils';
+
 import { IDisposable } from '@lumino/disposable';
 
 import { AttachedProperty } from '@lumino/properties';
@@ -53,6 +55,7 @@ export class DocumentManager implements IDocumentManager {
     this.services = options.manager;
     this._collaborative = !!options.collaborative;
     this._dialogs = options.sessionDialogs || sessionContextDialogs;
+    this._docProviderFactory = options.docProviderFactory;
 
     this._opener = options.opener;
     this._when = options.when || options.manager.ready;
@@ -483,7 +486,8 @@ export class DocumentManager implements IDocumentManager {
       modelDBFactory,
       setBusy: this._setBusy,
       sessionDialogs: this._dialogs,
-      collaborative: this._collaborative
+      collaborative: this._collaborative,
+      docProviderFactory: this._docProviderFactory
     });
     const handler = new SaveHandler({
       context,
@@ -610,6 +614,7 @@ export class DocumentManager implements IDocumentManager {
   private _when: Promise<void>;
   private _setBusy: (() => IDisposable) | undefined;
   private _dialogs: ISessionContext.IDialogs;
+  private _docProviderFactory: IDocumentProviderFactory | undefined;
   private _collaborative: boolean;
 }
 
@@ -655,6 +660,12 @@ export namespace DocumentManager {
      * The applicaton language translator.
      */
     translator?: ITranslator;
+
+    /**
+     * A factory method for the document provider.
+     */
+    docProviderFactory?: IDocumentProviderFactory;
+
     /**
      * Whether the context should be collaborative.
      * If true, the context will connect through yjs_ws_server to share information if possible.

+ 3 - 0
packages/docmanager/tsconfig.json

@@ -12,6 +12,9 @@
     {
       "path": "../coreutils"
     },
+    {
+      "path": "../docprovider"
+    },
     {
       "path": "../docregistry"
     },

+ 6 - 0
packages/docmanager/tsconfig.test.json

@@ -8,6 +8,9 @@
     {
       "path": "../coreutils"
     },
+    {
+      "path": "../docprovider"
+    },
     {
       "path": "../docregistry"
     },
@@ -32,6 +35,9 @@
     {
       "path": "../coreutils"
     },
+    {
+      "path": "../docprovider"
+    },
     {
       "path": "../docregistry"
     },

+ 54 - 0
packages/docprovider-extension/package.json

@@ -0,0 +1,54 @@
+{
+  "name": "@jupyterlab/docprovider-extension",
+  "version": "3.1.0-alpha.8",
+  "description": "JupyterLab - Document Provider Extension",
+  "homepage": "https://github.com/jupyterlab/jupyterlab",
+  "bugs": {
+    "url": "https://github.com/jupyterlab/jupyterlab/issues"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/jupyterlab/jupyterlab.git"
+  },
+  "license": "BSD-3-Clause",
+  "author": "Project Jupyter",
+  "sideEffects": [
+    "style/*.css",
+    "style/index.js"
+  ],
+  "main": "lib/index.js",
+  "types": "lib/index.d.ts",
+  "style": "style/index.css",
+  "directories": {
+    "lib": "lib/"
+  },
+  "files": [
+    "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
+    "schema/*.json",
+    "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
+    "style/index.js"
+  ],
+  "scripts": {
+    "build": "tsc -b",
+    "clean": "rimraf lib && rimraf tsconfig.tsbuildinfo",
+    "prepublishOnly": "npm run build",
+    "watch": "tsc -w --listEmittedFiles"
+  },
+  "dependencies": {
+    "@jupyterlab/application": "^3.1.0-alpha.8",
+    "@jupyterlab/coreutils": "^5.1.0-alpha.8",
+    "@jupyterlab/docprovider": "^3.1.0-alpha.8",
+    "@jupyterlab/services": "^6.1.0-alpha.8"
+  },
+  "devDependencies": {
+    "rimraf": "~3.0.0",
+    "typescript": "~4.1.3"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "jupyterlab": {
+    "extension": true
+  },
+  "styleModule": "style/index.js"
+}

+ 53 - 0
packages/docprovider-extension/src/index.ts

@@ -0,0 +1,53 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+/**
+ * @packageDocumentation
+ * @module docprovider-extension
+ */
+
+import {
+  JupyterFrontEnd,
+  JupyterFrontEndPlugin
+} from '@jupyterlab/application';
+
+import { PageConfig, URLExt } from '@jupyterlab/coreutils';
+
+import {
+  IDocumentProvider,
+  IDocumentProviderFactory,
+  ProviderMock,
+  WebSocketProviderWithLocks
+} from '@jupyterlab/docprovider';
+
+import { ServerConnection } from '@jupyterlab/services';
+
+/**
+ * The default document provider plugin
+ */
+const docProviderPlugin: JupyterFrontEndPlugin<IDocumentProviderFactory> = {
+  id: '@jupyterlab/docprovider-extension:plugin',
+  provides: IDocumentProviderFactory,
+  activate: (app: JupyterFrontEnd): IDocumentProviderFactory => {
+    const server = ServerConnection.makeSettings();
+    const url = URLExt.join(server.wsUrl, 'api/yjs');
+    const collaborative =
+      PageConfig.getOption('collaborative') == 'true' ? true : false;
+    const factory = (
+      options: IDocumentProviderFactory.IOptions
+    ): IDocumentProvider => {
+      return collaborative
+        ? new WebSocketProviderWithLocks({
+            ...options,
+            url
+          })
+        : new ProviderMock();
+    };
+    return factory;
+  }
+};
+
+/**
+ * Export the plugins as default.
+ */
+const plugins: JupyterFrontEndPlugin<any>[] = [docProviderPlugin];
+export default plugins;

+ 7 - 0
packages/docprovider-extension/style/index.css

@@ -0,0 +1,7 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) Jupyter Development Team.
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
+
+/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
+@import url('~@jupyterlab/application/style/index.css');

+ 7 - 0
packages/docprovider-extension/style/index.js

@@ -0,0 +1,7 @@
+/*-----------------------------------------------------------------------------
+| Copyright (c) Jupyter Development Team.
+| Distributed under the terms of the Modified BSD License.
+|----------------------------------------------------------------------------*/
+
+/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
+import '@jupyterlab/application/style/index.js';

+ 22 - 0
packages/docprovider-extension/tsconfig.json

@@ -0,0 +1,22 @@
+{
+  "extends": "../../tsconfigbase",
+  "compilerOptions": {
+    "outDir": "lib",
+    "rootDir": "src"
+  },
+  "include": ["src/**/*"],
+  "references": [
+    {
+      "path": "../application"
+    },
+    {
+      "path": "../coreutils"
+    },
+    {
+      "path": "../docprovider"
+    },
+    {
+      "path": "../services"
+    }
+  ]
+}

+ 1 - 0
packages/docprovider/package.json

@@ -39,6 +39,7 @@
   },
   "dependencies": {
     "@jupyterlab/shared-models": "^3.1.0-alpha.8",
+    "@lumino/coreutils": "^1.5.3",
     "lib0": "^0.2.41",
     "y-websocket": "^1.3.11",
     "yjs": "^13.5.3"

+ 1 - 11
packages/docprovider/src/index.ts

@@ -7,16 +7,6 @@
  * @module docprovider
  */
 
-export interface IProvider {
-  /**
-   * Resolves to true if the initial content has been initialized on the server. false otherwise.
-   */
-  requestInitialContent(): Promise<boolean>;
-  putInitializedState(): void;
-  acquireLock(): Promise<number>;
-  releaseLock(lock: number): void;
-  destroy(): void;
-}
-
 export * from './yprovider';
 export * from './mock';
+export * from './tokens';

+ 2 - 2
packages/docprovider/src/mock.ts

@@ -1,6 +1,6 @@
-import { IProvider } from './index';
+import { IDocumentProvider } from './index';
 
-export class ProviderMock implements IProvider {
+export class ProviderMock implements IDocumentProvider {
   requestInitialContent(): Promise<boolean> {
     return Promise.resolve(false);
   }

+ 70 - 0
packages/docprovider/src/tokens.ts

@@ -0,0 +1,70 @@
+import { Token } from '@lumino/coreutils';
+
+import { DocumentChange, YDocument } from '@jupyterlab/shared-models';
+
+/**
+ * The default document provider token.
+ */
+export const IDocumentProviderFactory = new Token<IDocumentProviderFactory>(
+  '@jupyterlab/docprovider:IDocumentProviderFactory'
+);
+
+/**
+ * An interface for a document provider.
+ */
+export interface IDocumentProvider {
+  /**
+   * Resolves to true if the initial content has been initialized on the server. false otherwise.
+   */
+  requestInitialContent(): Promise<boolean>;
+
+  /**
+   * Put the initialized state.
+   */
+  putInitializedState(): void;
+
+  /**
+   * Acquire a lock.
+   * Returns a Promise that resolves to the lock number.
+   */
+  acquireLock(): Promise<number>;
+
+  /**
+   * Release a lock.
+   *
+   * @param lock The lock to release.
+   */
+  releaseLock(lock: number): void;
+
+  /**
+   * Destroy the provider.
+   */
+  destroy(): void;
+}
+
+/**
+ * The type for the document provider factory.
+ */
+export type IDocumentProviderFactory = (
+  options: IDocumentProviderFactory.IOptions
+) => IDocumentProvider;
+
+/**
+ * A namespace for IDocumentProviderFactory statics.
+ */
+export namespace IDocumentProviderFactory {
+  /**
+   * The instantiation options for a IDocumentProviderFactory.
+   */
+  export interface IOptions {
+    /**
+     * The name (id) of the room
+     */
+    guid: string;
+
+    /**
+     * The YNotebook.
+     */
+    ymodel: YDocument<DocumentChange>;
+  }
+}

+ 11 - 21
packages/docprovider/src/yprovider.ts

@@ -3,8 +3,6 @@
 | Distributed under the terms of the Modified BSD License.
 |----------------------------------------------------------------------------*/
 
-import * as sharedModels from '@jupyterlab/shared-models';
-
 import * as Y from 'yjs';
 
 import { WebsocketProvider } from 'y-websocket';
@@ -13,16 +11,18 @@ import * as decoding from 'lib0/decoding';
 
 import * as encoding from 'lib0/encoding';
 
+import { IDocumentProviderFactory } from './tokens';
+
 /**
  * A class to provide Yjs synchronization over Websocket.
  */
-export class WebsocketProviderWithLocks extends WebsocketProvider {
+export class WebSocketProviderWithLocks extends WebsocketProvider {
   /**
-   * Construct a new WebsocketProviderWithLocks
+   * Construct a new WebSocketProviderWithLocks
    *
-   * @param options The instantiation options for a WebsocketProviderWithLocks
+   * @param options The instantiation options for a WebSocketProviderWithLocks
    */
-  constructor(options: WebsocketProviderWithLocks.IOptions) {
+  constructor(options: WebSocketProviderWithLocks.IOptions) {
     super(options.url, options.guid, options.ymodel.ydoc, {
       awareness: options.ymodel.awareness
     });
@@ -176,26 +176,16 @@ export class WebsocketProviderWithLocks extends WebsocketProvider {
 }
 
 /**
- * A namespace for WebsocketProviderWithLocks statics.
+ * A namespace for WebSocketProviderWithLocks statics.
  */
-export namespace WebsocketProviderWithLocks {
+export namespace WebSocketProviderWithLocks {
   /**
-   * The instantiation options for a WebsocketProviderWithLocks.
+   * The instantiation options for a WebSocketProviderWithLocks.
    */
-  export interface IOptions {
+  export interface IOptions extends IDocumentProviderFactory.IOptions {
     /**
-     * The server URL.
+     * The server URL
      */
     url: string;
-
-    /**
-     * The name of the room
-     */
-    guid: string;
-
-    /**
-     * The YNotebook.
-     */
-    ymodel: sharedModels.YDocument<sharedModels.DocumentChange>;
   }
 }

+ 2 - 2
packages/docprovider/test/yprovider.spec.ts

@@ -1,12 +1,12 @@
 // Copyright (c) Jupyter Development Team.
 // Distributed under the terms of the Modified BSD License.
 
-import { WebsocketProviderWithLocks } from '../src';
+import { WebSocketProviderWithLocks } from '../src';
 
 describe('@jupyterlab/docprovider', () => {
   describe('docprovider', () => {
     it('should have a type', () => {
-      expect(WebsocketProviderWithLocks).not.toBeUndefined();
+      expect(WebSocketProviderWithLocks).not.toBeUndefined();
     });
   });
 });

+ 13 - 14
packages/docregistry/src/context.ts

@@ -7,6 +7,8 @@ import {
   ServerConnection
 } from '@jupyterlab/services';
 
+import { IDocumentProviderFactory } from '@jupyterlab/docprovider';
+
 import * as ymodels from '@jupyterlab/shared-models';
 
 import * as Y from 'yjs';
@@ -28,7 +30,7 @@ import {
   sessionContextDialogs
 } from '@jupyterlab/apputils';
 
-import { PageConfig, PathExt, URLExt } from '@jupyterlab/coreutils';
+import { PathExt } from '@jupyterlab/coreutils';
 
 import { IModelDB, ModelDB } from '@jupyterlab/observables';
 
@@ -42,11 +44,7 @@ import {
   TranslationBundle
 } from '@jupyterlab/translation';
 
-import {
-  WebsocketProviderWithLocks,
-  IProvider,
-  ProviderMock
-} from '@jupyterlab/docprovider';
+import { IDocumentProvider, ProviderMock } from '@jupyterlab/docprovider';
 
 import { DocumentRegistry } from './registry';
 
@@ -85,14 +83,10 @@ export class Context<
     const ydoc = ymodel.ydoc;
     this._ydoc = ydoc;
     this._ycontext = ydoc.getMap('context');
-    // @todo remove websocket provider - this should be handled by a separate plugin
-    const server = ServerConnection.makeSettings();
-    const url = URLExt.join(server.wsUrl, 'api/yjs');
     const guid = this._factory.contentType + ':' + localPath;
-    const collaborative =
-      PageConfig.getOption('collaborative') == 'true' ? true : false;
-    this._provider = collaborative
-      ? new WebsocketProviderWithLocks({ url, guid, ymodel })
+    const docProviderFactory = options.docProviderFactory;
+    this._provider = docProviderFactory
+      ? docProviderFactory({ guid, ymodel })
       : new ProviderMock();
 
     this._readyPromise = manager.ready.then(() => {
@@ -886,7 +880,7 @@ or load the version on disk (revert)?`,
   private _saveState = new Signal<this, DocumentRegistry.SaveState>(this);
   private _disposed = new Signal<this, void>(this);
   private _dialogs: ISessionContext.IDialogs;
-  private _provider: IProvider;
+  private _provider: IDocumentProvider;
   private _ydoc: Y.Doc;
   private _ycontext: Y.Map<string>;
 }
@@ -924,6 +918,11 @@ export namespace Context {
      */
     kernelPreference?: ISessionContext.IKernelPreference;
 
+    /**
+     * An factory method for the document provider.
+     */
+    docProviderFactory?: IDocumentProviderFactory;
+
     /**
      * An IModelDB factory method which may be used for the document.
      */

+ 1 - 0
packages/metapackage/package.json

@@ -54,6 +54,7 @@
     "@jupyterlab/docmanager": "^3.1.0-alpha.8",
     "@jupyterlab/docmanager-extension": "^3.1.0-alpha.8",
     "@jupyterlab/docprovider": "^3.1.0-alpha.8",
+    "@jupyterlab/docprovider-extension": "^3.1.0-alpha.8",
     "@jupyterlab/docregistry": "^3.1.0-alpha.8",
     "@jupyterlab/documentsearch": "^3.1.0-alpha.8",
     "@jupyterlab/documentsearch-extension": "^3.1.0-alpha.8",

+ 3 - 0
packages/metapackage/tsconfig.json

@@ -75,6 +75,9 @@
     {
       "path": "../docprovider"
     },
+    {
+      "path": "../docprovider-extension"
+    },
     {
       "path": "../docregistry"
     },

+ 3 - 0
tsconfigdoc.json

@@ -79,6 +79,9 @@
     {
       "path": "./packages/docprovider"
     },
+    {
+      "path": "./packages/docprovider-extension"
+    },
     {
       "path": "./packages/docregistry"
     },