@jupyter/docprovider 4.2.1 → 4.3.0-beta.0
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/tokens.d.ts +6 -0
- package/lib/ydrive.js +7 -0
- package/lib/yprovider.d.ts +3 -0
- package/lib/yprovider.js +76 -5
- package/package.json +3 -2
package/lib/tokens.d.ts
CHANGED
|
@@ -101,3 +101,9 @@ export interface IForkManager extends IDisposable {
|
|
|
101
101
|
* Token providing a fork manager instance.
|
|
102
102
|
*/
|
|
103
103
|
export declare const IForkManagerToken: Token<IForkManager>;
|
|
104
|
+
export interface ISessionClosePayload {
|
|
105
|
+
reason: 'unknown_session' | 'version_mismatch' | 'initialization_error';
|
|
106
|
+
sessionId?: string;
|
|
107
|
+
reloadable?: boolean;
|
|
108
|
+
errorReason?: string;
|
|
109
|
+
}
|
package/lib/ydrive.js
CHANGED
|
@@ -191,6 +191,13 @@ export class RtcContentProvider {
|
|
|
191
191
|
const key = `${options.format}:${options.type}:${localPath}`;
|
|
192
192
|
const provider = this._providers.get(key);
|
|
193
193
|
if (provider) {
|
|
194
|
+
// The default jupyter-server REST content provider would ask
|
|
195
|
+
// for content here; this would trigger any encoding errors
|
|
196
|
+
// leading to fast-rejection. However we do not ask for
|
|
197
|
+
// content as it is synced over a websocket instead,
|
|
198
|
+
// this means that the errors do not propagate.
|
|
199
|
+
// Instead we handle it by ensuring that the `ready` promise
|
|
200
|
+
// rejection (due to websocket closure ahead of sync) propagates.
|
|
194
201
|
// If the document doesn't exist, `super.get` will reject with an
|
|
195
202
|
// error and the provider will never be resolved.
|
|
196
203
|
// Use `Promise.all` to reject as soon as possible. The Context will
|
package/lib/yprovider.d.ts
CHANGED
|
@@ -38,8 +38,10 @@ export declare class WebSocketProvider implements IDocumentProvider, IForkProvid
|
|
|
38
38
|
get wsProvider(): YWebsocketProvider | null;
|
|
39
39
|
private _disconnect;
|
|
40
40
|
private _onUserChanged;
|
|
41
|
+
private _buildSessionExpiredMessage;
|
|
41
42
|
private _onConnectionClosed;
|
|
42
43
|
private _onSync;
|
|
44
|
+
private _getCloseReasonMessage;
|
|
43
45
|
private _awareness;
|
|
44
46
|
private _contentType;
|
|
45
47
|
private _format;
|
|
@@ -51,6 +53,7 @@ export declare class WebSocketProvider implements IDocumentProvider, IForkProvid
|
|
|
51
53
|
private _yWebsocketProvider;
|
|
52
54
|
private _serverSettings;
|
|
53
55
|
private _trans;
|
|
56
|
+
private _hasSynced;
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
56
59
|
* A namespace for WebSocketProvider statics.
|
package/lib/yprovider.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
| Copyright (c) Jupyter Development Team.
|
|
3
3
|
| Distributed under the terms of the Modified BSD License.
|
|
4
4
|
|----------------------------------------------------------------------------*/
|
|
5
|
-
import {
|
|
5
|
+
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
6
6
|
import { ServerConnection } from '@jupyterlab/services';
|
|
7
7
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
8
8
|
import { Signal } from '@lumino/signaling';
|
|
@@ -27,12 +27,46 @@ export class WebSocketProvider {
|
|
|
27
27
|
*/
|
|
28
28
|
constructor(options) {
|
|
29
29
|
var _a;
|
|
30
|
-
this._onConnectionClosed = (event) => {
|
|
30
|
+
this._onConnectionClosed = async (event) => {
|
|
31
|
+
if ([4400, 4404, 4500].includes(event.code)) {
|
|
32
|
+
if (!this._hasSynced) {
|
|
33
|
+
// Rejecting the ready promise will close the file placeholder widget.
|
|
34
|
+
const reason = this._getCloseReasonMessage(event.code);
|
|
35
|
+
this._ready.reject(reason);
|
|
36
|
+
// Disposing model prevents repeated websocket reconnection attempts.
|
|
37
|
+
// Rejecting the ready promise will ultimately close the file,
|
|
38
|
+
// but the document manager takes some time to do so.
|
|
39
|
+
this._sharedModel.dispose();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
31
42
|
if (event.code === 1003) {
|
|
32
43
|
console.error('Document provider closed:', event.reason);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
let payload;
|
|
45
|
+
try {
|
|
46
|
+
payload = JSON.parse(event.reason);
|
|
47
|
+
}
|
|
48
|
+
catch (_a) {
|
|
49
|
+
payload = {
|
|
50
|
+
reason: 'unknown_session',
|
|
51
|
+
sessionId: '',
|
|
52
|
+
reloadable: false,
|
|
53
|
+
errorReason: event.reason
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const { title, body } = this._buildSessionExpiredMessage(payload, this._trans);
|
|
57
|
+
const result = await showDialog({
|
|
58
|
+
title,
|
|
59
|
+
body,
|
|
60
|
+
buttons: payload.reloadable
|
|
61
|
+
? [
|
|
62
|
+
Dialog.cancelButton({ label: this._trans.__('Continue') }),
|
|
63
|
+
Dialog.okButton({ label: this._trans.__('Reload') })
|
|
64
|
+
]
|
|
65
|
+
: [Dialog.okButton({ label: this._trans.__('Ok') })]
|
|
66
|
+
});
|
|
67
|
+
if (result.button.accept && payload.reloadable) {
|
|
68
|
+
window.location.reload();
|
|
69
|
+
}
|
|
36
70
|
// Dispose shared model immediately. Better break the document model,
|
|
37
71
|
// than overriding data on disk.
|
|
38
72
|
this._sharedModel.dispose();
|
|
@@ -40,6 +74,7 @@ export class WebSocketProvider {
|
|
|
40
74
|
};
|
|
41
75
|
this._onSync = (isSynced) => {
|
|
42
76
|
if (isSynced) {
|
|
77
|
+
this._hasSynced = true;
|
|
43
78
|
if (this._yWebsocketProvider) {
|
|
44
79
|
this._yWebsocketProvider.off('sync', this._onSync);
|
|
45
80
|
const state = this._sharedModel.ydoc.getMap('state');
|
|
@@ -49,6 +84,7 @@ export class WebSocketProvider {
|
|
|
49
84
|
}
|
|
50
85
|
};
|
|
51
86
|
this._ready = new PromiseDelegate();
|
|
87
|
+
this._hasSynced = false;
|
|
52
88
|
this._isDisposed = false;
|
|
53
89
|
this._path = options.path;
|
|
54
90
|
this._contentType = options.contentType;
|
|
@@ -153,4 +189,39 @@ export class WebSocketProvider {
|
|
|
153
189
|
_onUserChanged(user) {
|
|
154
190
|
this._awareness.setLocalStateField('user', user.identity);
|
|
155
191
|
}
|
|
192
|
+
_buildSessionExpiredMessage(payload, trans) {
|
|
193
|
+
switch (payload.reason) {
|
|
194
|
+
case 'version_mismatch':
|
|
195
|
+
return {
|
|
196
|
+
title: trans.__('Collaboration extension updated'),
|
|
197
|
+
body: trans.__('Reload the browser tab to load the new version.')
|
|
198
|
+
};
|
|
199
|
+
case 'initialization_error':
|
|
200
|
+
return {
|
|
201
|
+
title: trans.__('Document error'),
|
|
202
|
+
body: trans.__('Failed to initialize the document. Close this tab and reopen the file.')
|
|
203
|
+
};
|
|
204
|
+
case 'unknown_session':
|
|
205
|
+
default:
|
|
206
|
+
return {
|
|
207
|
+
title: trans.__('Session expired'),
|
|
208
|
+
body: payload.errorReason
|
|
209
|
+
? trans.__(payload.errorReason)
|
|
210
|
+
: trans.__('Reload the browser tab to continue.')
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
_getCloseReasonMessage(code) {
|
|
215
|
+
switch (code) {
|
|
216
|
+
case 4400: {
|
|
217
|
+
return this._trans.__('Bad request for %1', this._path);
|
|
218
|
+
}
|
|
219
|
+
case 4404: {
|
|
220
|
+
return this._trans.__('Could not find %1', this._path);
|
|
221
|
+
}
|
|
222
|
+
case 4500: {
|
|
223
|
+
return this._trans.__('Internal server error when loading %1', this._path);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
156
227
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyter/docprovider",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0-beta.0",
|
|
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/collaborative-drive": "^4.
|
|
44
|
+
"@jupyter/collaborative-drive": "^4.3.0-beta.0",
|
|
45
45
|
"@jupyter/ydoc": "^2.1.3 || ^3.0.0",
|
|
46
46
|
"@jupyterlab/apputils": "^4.5.0",
|
|
47
47
|
"@jupyterlab/cells": "^4.5.0",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@jupyterlab/testing": "^4.5.0",
|
|
62
|
+
"@jupyterlab/testutils": "^4.5.0",
|
|
62
63
|
"@types/jest": "^29.2.0",
|
|
63
64
|
"jest": "^29.5.0",
|
|
64
65
|
"rimraf": "^4.1.2",
|