@jupyter/docprovider 1.0.0-alpha.1 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/requests.d.ts +8 -0
- package/lib/requests.js +25 -0
- package/lib/ydrive.d.ts +3 -1
- package/lib/ydrive.js +8 -6
- package/lib/yprovider.d.ts +10 -2
- package/lib/yprovider.js +32 -30
- package/package.json +2 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Contents } from '@jupyterlab/services';
|
|
2
|
+
export interface ISessionModel {
|
|
3
|
+
format: Contents.FileFormat;
|
|
4
|
+
type: Contents.ContentType;
|
|
5
|
+
fileId: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function requestDocSession(format: string, type: string, path: string): Promise<ISessionModel>;
|
package/lib/requests.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* -----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|----------------------------------------------------------------------------*/
|
|
5
|
+
import { URLExt } from '@jupyterlab/coreutils';
|
|
6
|
+
import { ServerConnection } from '@jupyterlab/services';
|
|
7
|
+
/**
|
|
8
|
+
* Document session endpoint provided by `jupyter_collaboration`
|
|
9
|
+
* See https://github.com/jupyterlab/jupyter_collaboration
|
|
10
|
+
*/
|
|
11
|
+
const DOC_SESSION_URL = 'api/collaboration/session';
|
|
12
|
+
export async function requestDocSession(format, type, path) {
|
|
13
|
+
const { makeSettings, makeRequest, ResponseError } = ServerConnection;
|
|
14
|
+
const settings = makeSettings();
|
|
15
|
+
const url = URLExt.join(settings.baseUrl, DOC_SESSION_URL, encodeURIComponent(path));
|
|
16
|
+
const data = {
|
|
17
|
+
method: 'PUT',
|
|
18
|
+
body: JSON.stringify({ format, type })
|
|
19
|
+
};
|
|
20
|
+
const response = await makeRequest(url, data, settings);
|
|
21
|
+
if (response.status !== 200 && response.status !== 201) {
|
|
22
|
+
throw new ResponseError(response);
|
|
23
|
+
}
|
|
24
|
+
return response.json();
|
|
25
|
+
}
|
package/lib/ydrive.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TranslationBundle } from '@jupyterlab/translation';
|
|
1
2
|
import { Contents, Drive, User } from '@jupyterlab/services';
|
|
2
3
|
/**
|
|
3
4
|
* A Collaborative implementation for an `IDrive`, talking to the
|
|
@@ -9,7 +10,7 @@ export declare class YDrive extends Drive {
|
|
|
9
10
|
*
|
|
10
11
|
* @param user - The user manager to add the identity to the awareness of documents.
|
|
11
12
|
*/
|
|
12
|
-
constructor(user: User.IManager);
|
|
13
|
+
constructor(user: User.IManager, translator: TranslationBundle);
|
|
13
14
|
/**
|
|
14
15
|
* SharedModel factory for the YDrive.
|
|
15
16
|
*/
|
|
@@ -73,5 +74,6 @@ export declare class YDrive extends Drive {
|
|
|
73
74
|
save(localPath: string, options?: Partial<Contents.IModel>): Promise<Contents.IModel>;
|
|
74
75
|
private _onCreate;
|
|
75
76
|
private _user;
|
|
77
|
+
private _trans;
|
|
76
78
|
private _providers;
|
|
77
79
|
}
|
package/lib/ydrive.js
CHANGED
|
@@ -7,7 +7,7 @@ import { WebSocketProvider } from './yprovider';
|
|
|
7
7
|
/**
|
|
8
8
|
* The url for the default drive service.
|
|
9
9
|
*/
|
|
10
|
-
const
|
|
10
|
+
const DOCUMENT_PROVIDER_URL = 'api/collaboration/room';
|
|
11
11
|
/**
|
|
12
12
|
* A Collaborative implementation for an `IDrive`, talking to the
|
|
13
13
|
* server using the Jupyter REST API and a WebSocket connection.
|
|
@@ -18,7 +18,7 @@ export class YDrive extends Drive {
|
|
|
18
18
|
*
|
|
19
19
|
* @param user - The user manager to add the identity to the awareness of documents.
|
|
20
20
|
*/
|
|
21
|
-
constructor(user) {
|
|
21
|
+
constructor(user, translator) {
|
|
22
22
|
super({ name: 'YDrive' });
|
|
23
23
|
this._onCreate = (options, sharedModel) => {
|
|
24
24
|
if (typeof options.format !== 'string') {
|
|
@@ -26,14 +26,15 @@ export class YDrive extends Drive {
|
|
|
26
26
|
}
|
|
27
27
|
try {
|
|
28
28
|
const provider = new WebSocketProvider({
|
|
29
|
-
url: URLExt.join(this.serverSettings.wsUrl,
|
|
29
|
+
url: URLExt.join(this.serverSettings.wsUrl, DOCUMENT_PROVIDER_URL),
|
|
30
30
|
path: options.path,
|
|
31
31
|
format: options.format,
|
|
32
32
|
contentType: options.contentType,
|
|
33
33
|
model: sharedModel,
|
|
34
|
-
user: this._user
|
|
34
|
+
user: this._user,
|
|
35
|
+
translator: this._trans
|
|
35
36
|
});
|
|
36
|
-
const key = `${options.
|
|
37
|
+
const key = `${options.format}:${options.contentType}:${options.path}`;
|
|
37
38
|
this._providers.set(key, provider);
|
|
38
39
|
sharedModel.disposed.connect(() => {
|
|
39
40
|
const provider = this._providers.get(key);
|
|
@@ -50,6 +51,7 @@ export class YDrive extends Drive {
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
this._user = user;
|
|
54
|
+
this._trans = translator;
|
|
53
55
|
this._providers = new Map();
|
|
54
56
|
this.sharedModelFactory = new SharedModelFactory(this._onCreate);
|
|
55
57
|
}
|
|
@@ -93,7 +95,7 @@ export class YDrive extends Drive {
|
|
|
93
95
|
*/
|
|
94
96
|
async get(localPath, options) {
|
|
95
97
|
if (options && options.format && options.type) {
|
|
96
|
-
const key = `${options.
|
|
98
|
+
const key = `${options.format}:${options.type}:${localPath}`;
|
|
97
99
|
const provider = this._providers.get(key);
|
|
98
100
|
if (provider) {
|
|
99
101
|
const model = super.get(localPath, { ...options, content: false });
|
package/lib/yprovider.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { User } from '@jupyterlab/services';
|
|
2
|
-
import {
|
|
2
|
+
import { TranslationBundle } from '@jupyterlab/translation';
|
|
3
3
|
import { IDisposable } from '@lumino/disposable';
|
|
4
|
+
import { DocumentChange, YDocument } from '@jupyter/ydoc';
|
|
4
5
|
/**
|
|
5
6
|
* An interface for a document provider.
|
|
6
7
|
*/
|
|
@@ -35,7 +36,9 @@ export declare class WebSocketProvider implements IDocumentProvider {
|
|
|
35
36
|
* Dispose of the resources held by the object.
|
|
36
37
|
*/
|
|
37
38
|
dispose(): void;
|
|
39
|
+
private _connect;
|
|
38
40
|
private _onUserChanged;
|
|
41
|
+
private _onConnectionClosed;
|
|
39
42
|
private _awareness;
|
|
40
43
|
private _contentType;
|
|
41
44
|
private _format;
|
|
@@ -43,8 +46,9 @@ export declare class WebSocketProvider implements IDocumentProvider {
|
|
|
43
46
|
private _path;
|
|
44
47
|
private _ready;
|
|
45
48
|
private _serverUrl;
|
|
46
|
-
private
|
|
49
|
+
private _sharedModel;
|
|
47
50
|
private _yWebsocketProvider;
|
|
51
|
+
private _trans;
|
|
48
52
|
}
|
|
49
53
|
/**
|
|
50
54
|
* A namespace for WebSocketProvider statics.
|
|
@@ -78,5 +82,9 @@ export declare namespace WebSocketProvider {
|
|
|
78
82
|
* The user data
|
|
79
83
|
*/
|
|
80
84
|
user: User.IManager;
|
|
85
|
+
/**
|
|
86
|
+
* The jupyterlab translator
|
|
87
|
+
*/
|
|
88
|
+
translator: TranslationBundle;
|
|
81
89
|
}
|
|
82
90
|
}
|
package/lib/yprovider.js
CHANGED
|
@@ -2,16 +2,11 @@
|
|
|
2
2
|
| Copyright (c) Jupyter Development Team.
|
|
3
3
|
| Distributed under the terms of the Modified BSD License.
|
|
4
4
|
|----------------------------------------------------------------------------*/
|
|
5
|
-
import {
|
|
6
|
-
import { ServerConnection } from '@jupyterlab/services';
|
|
5
|
+
import { showErrorMessage, Dialog } from '@jupyterlab/apputils';
|
|
7
6
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
8
7
|
import { Signal } from '@lumino/signaling';
|
|
9
8
|
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
10
|
-
|
|
11
|
-
* Room Id endpoint provided by `jupyter_collaboration`
|
|
12
|
-
* See https://github.com/jupyterlab/jupyter_collaboration
|
|
13
|
-
*/
|
|
14
|
-
const FILE_PATH_TO_ROOM_ID_URL = 'api/yjs/roomid';
|
|
9
|
+
import { requestDocSession } from './requests';
|
|
15
10
|
/**
|
|
16
11
|
* A class to provide Yjs synchronization over WebSocket.
|
|
17
12
|
*
|
|
@@ -25,15 +20,27 @@ export class WebSocketProvider {
|
|
|
25
20
|
* @param options The instantiation options for a WebSocketProvider
|
|
26
21
|
*/
|
|
27
22
|
constructor(options) {
|
|
23
|
+
this._onConnectionClosed = (event) => {
|
|
24
|
+
if (event.code === 1003) {
|
|
25
|
+
console.error('Document provider closed:', event.reason);
|
|
26
|
+
showErrorMessage(this._trans.__('Session expired'), this._trans.__('The document session expired. You need to reload this browser tab.'), [Dialog.okButton({ label: this._trans.__('Reload') })])
|
|
27
|
+
.then(r => window.location.reload())
|
|
28
|
+
.catch(e => window.location.reload());
|
|
29
|
+
// Dispose shared model immediately. Better break the document model,
|
|
30
|
+
// than overriding data on disk.
|
|
31
|
+
this._sharedModel.dispose();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
28
34
|
this._ready = new PromiseDelegate();
|
|
29
35
|
this._isDisposed = false;
|
|
30
36
|
this._path = options.path;
|
|
31
37
|
this._contentType = options.contentType;
|
|
32
38
|
this._format = options.format;
|
|
33
39
|
this._serverUrl = options.url;
|
|
34
|
-
this.
|
|
40
|
+
this._sharedModel = options.model;
|
|
35
41
|
this._awareness = options.model.awareness;
|
|
36
42
|
this._yWebsocketProvider = null;
|
|
43
|
+
this._trans = options.translator;
|
|
37
44
|
const user = options.user;
|
|
38
45
|
user.ready
|
|
39
46
|
.then(() => {
|
|
@@ -41,26 +48,7 @@ export class WebSocketProvider {
|
|
|
41
48
|
})
|
|
42
49
|
.catch(e => console.error(e));
|
|
43
50
|
user.userChanged.connect(this._onUserChanged, this);
|
|
44
|
-
|
|
45
|
-
const url = URLExt.join(serverSettings.baseUrl, FILE_PATH_TO_ROOM_ID_URL, encodeURIComponent(this._path));
|
|
46
|
-
const data = {
|
|
47
|
-
method: 'PUT',
|
|
48
|
-
body: JSON.stringify({ format: this._format, type: this._contentType })
|
|
49
|
-
};
|
|
50
|
-
ServerConnection.makeRequest(url, data, serverSettings)
|
|
51
|
-
.then(response => {
|
|
52
|
-
if (response.status !== 200 && response.status !== 201) {
|
|
53
|
-
throw new ServerConnection.ResponseError(response);
|
|
54
|
-
}
|
|
55
|
-
return response.text();
|
|
56
|
-
})
|
|
57
|
-
.then(roomid => {
|
|
58
|
-
this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, roomid, this._ydoc, {
|
|
59
|
-
awareness: this._awareness
|
|
60
|
-
});
|
|
61
|
-
})
|
|
62
|
-
.then(() => this._ready.resolve())
|
|
63
|
-
.catch(reason => console.warn(reason));
|
|
51
|
+
this._connect();
|
|
64
52
|
}
|
|
65
53
|
/**
|
|
66
54
|
* Test whether the object has been disposed.
|
|
@@ -78,14 +66,28 @@ export class WebSocketProvider {
|
|
|
78
66
|
* Dispose of the resources held by the object.
|
|
79
67
|
*/
|
|
80
68
|
dispose() {
|
|
81
|
-
var _a;
|
|
69
|
+
var _a, _b;
|
|
82
70
|
if (this.isDisposed) {
|
|
83
71
|
return;
|
|
84
72
|
}
|
|
85
73
|
this._isDisposed = true;
|
|
86
|
-
(_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.
|
|
74
|
+
(_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.off('connection-close', this._onConnectionClosed);
|
|
75
|
+
(_b = this._yWebsocketProvider) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
87
76
|
Signal.clearData(this);
|
|
88
77
|
}
|
|
78
|
+
_connect() {
|
|
79
|
+
requestDocSession(this._format, this._contentType, this._path)
|
|
80
|
+
.then((session) => {
|
|
81
|
+
this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, `${session.format}:${session.type}:${session.fileId}`, this._sharedModel.ydoc, {
|
|
82
|
+
disableBc: true,
|
|
83
|
+
params: { sessionId: session.sessionId },
|
|
84
|
+
awareness: this._awareness
|
|
85
|
+
});
|
|
86
|
+
this._yWebsocketProvider.on('connection-close', this._onConnectionClosed);
|
|
87
|
+
})
|
|
88
|
+
.then(r => this._ready.resolve())
|
|
89
|
+
.catch(e => console.warn(e));
|
|
90
|
+
}
|
|
89
91
|
_onUserChanged(user) {
|
|
90
92
|
this._awareness.setLocalStateField('user', user.identity);
|
|
91
93
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyter/docprovider",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.2",
|
|
4
4
|
"description": "JupyterLab - Document Provider",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyter_collaboration",
|
|
6
6
|
"bugs": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"watch": "tsc -b --watch"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@jupyter/ydoc": "^0.3.
|
|
44
|
+
"@jupyter/ydoc": "^0.3.4",
|
|
45
45
|
"@jupyterlab/coreutils": "^6.0.0-alpha.19",
|
|
46
46
|
"@jupyterlab/services": "^7.0.0-alpha.19",
|
|
47
47
|
"@lumino/coreutils": "^2.0.0-beta.0",
|