@jupyter/docprovider 4.2.0 → 4.3.0-alpha.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.
@@ -1,12 +1,14 @@
1
1
  import { ReactWidget } from '@jupyterlab/apputils';
2
2
  import { IForkProvider } from './ydrive';
3
+ import { ServerConnection } from '@jupyterlab/services';
3
4
  export declare class TimelineWidget extends ReactWidget {
4
5
  private apiURL;
5
6
  private provider;
6
7
  private contentType;
7
8
  private format;
8
9
  private documentTimelineUrl;
9
- constructor(apiURL: string, provider: IForkProvider, contentType: string, format: string, documentTimelineUrl: string);
10
+ private _serverSettings?;
11
+ constructor(apiURL: string, provider: IForkProvider, contentType: string, format: string, documentTimelineUrl: string, serverSettings?: ServerConnection.ISettings);
10
12
  render(): JSX.Element;
11
13
  updateContent(apiURL: string, provider: IForkProvider): void;
12
14
  }
@@ -6,17 +6,18 @@ import { ReactWidget } from '@jupyterlab/apputils';
6
6
  import { TimelineSliderComponent } from './component';
7
7
  import * as React from 'react';
8
8
  export class TimelineWidget extends ReactWidget {
9
- constructor(apiURL, provider, contentType, format, documentTimelineUrl) {
9
+ constructor(apiURL, provider, contentType, format, documentTimelineUrl, serverSettings) {
10
10
  super();
11
11
  this.apiURL = apiURL;
12
12
  this.provider = provider;
13
13
  this.contentType = contentType;
14
14
  this.format = format;
15
15
  this.documentTimelineUrl = documentTimelineUrl;
16
+ this._serverSettings = serverSettings;
16
17
  this.addClass('jp-timelineSliderWrapper');
17
18
  }
18
19
  render() {
19
- return (React.createElement(TimelineSliderComponent, { key: this.apiURL, apiURL: this.apiURL, provider: this.provider, contentType: this.contentType, format: this.format, documentTimelineUrl: this.documentTimelineUrl }));
20
+ return (React.createElement(TimelineSliderComponent, { key: this.apiURL, apiURL: this.apiURL, provider: this.provider, contentType: this.contentType, format: this.format, documentTimelineUrl: this.documentTimelineUrl, serverSettings: this._serverSettings }));
20
21
  }
21
22
  updateContent(apiURL, provider) {
22
23
  this.apiURL = apiURL;
@@ -1,12 +1,14 @@
1
1
  import React from 'react';
2
2
  import '../style/slider.css';
3
3
  import { IForkProvider } from './ydrive';
4
+ import { ServerConnection } from '@jupyterlab/services';
4
5
  type Props = {
5
6
  apiURL: string;
6
7
  provider: IForkProvider;
7
8
  contentType: string;
8
9
  format: string;
9
10
  documentTimelineUrl: string;
11
+ serverSettings?: ServerConnection.ISettings;
10
12
  };
11
13
  export declare const TimelineSliderComponent: React.FC<Props>;
12
14
  export {};
package/lib/component.js CHANGED
@@ -7,7 +7,7 @@ import '../style/slider.css';
7
7
  import { requestUndoRedo, requestDocSession, requestDocumentTimeline } from './requests';
8
8
  import { historyIcon } from '@jupyterlab/ui-components';
9
9
  import { Notification } from '@jupyterlab/apputils';
10
- export const TimelineSliderComponent = ({ apiURL, provider, contentType, format, documentTimelineUrl }) => {
10
+ export const TimelineSliderComponent = ({ apiURL, provider, contentType, format, documentTimelineUrl, serverSettings }) => {
11
11
  const [data, setData] = useState({
12
12
  roomId: '',
13
13
  timestamps: [],
@@ -23,7 +23,7 @@ export const TimelineSliderComponent = ({ apiURL, provider, contentType, format,
23
23
  async function fetchTimeline(notebookPath) {
24
24
  try {
25
25
  if (isFirstChange.current) {
26
- const response = await requestDocumentTimeline(format, contentType, notebookPath);
26
+ const response = await requestDocumentTimeline(format, contentType, notebookPath, serverSettings);
27
27
  if (!response.ok) {
28
28
  if (response.status === 404) {
29
29
  throw new Error('Not found');
@@ -43,7 +43,7 @@ export const TimelineSliderComponent = ({ apiURL, provider, contentType, format,
43
43
  setData(data);
44
44
  setCurrentTimestampIndex(data.timestamps.length - 1);
45
45
  provider.connectToForkDoc(data.forkRoom, data.sessionId);
46
- sessionRef.current = await requestDocSession(format, contentType, extractFilenameFromURL(apiURL));
46
+ sessionRef.current = await requestDocSession(format, contentType, extractFilenameFromURL(apiURL), serverSettings);
47
47
  }
48
48
  setToggle(true);
49
49
  isFirstChange.current = false;
@@ -59,7 +59,7 @@ export const TimelineSliderComponent = ({ apiURL, provider, contentType, format,
59
59
  console.error('Session is not initialized');
60
60
  return;
61
61
  }
62
- const response = await requestUndoRedo(`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`, 'restore', 0, data.forkRoom);
62
+ const response = await requestUndoRedo(`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`, 'restore', 0, data.forkRoom, serverSettings);
63
63
  if (response.code === 200) {
64
64
  Notification.success(response.status, { autoClose: 4000 });
65
65
  provider.reconnect();
@@ -84,7 +84,7 @@ export const TimelineSliderComponent = ({ apiURL, provider, contentType, format,
84
84
  console.error('Session is not initialized');
85
85
  return;
86
86
  }
87
- await requestUndoRedo(`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`, action, steps, data.forkRoom);
87
+ await requestUndoRedo(`${sessionRef.current.format}:${sessionRef.current.type}:${sessionRef.current.fileId}`, action, steps, data.forkRoom, serverSettings);
88
88
  }
89
89
  catch (error) {
90
90
  console.error('Error fetching or applying updates:', error);
@@ -1,5 +1,5 @@
1
1
  import { ICollaborativeContentProvider } from '@jupyter/collaborative-drive';
2
- import { Event } from '@jupyterlab/services';
2
+ import { Event, ServerConnection } from '@jupyterlab/services';
3
3
  import { ISignal } from '@lumino/signaling';
4
4
  import { IAllForksResponse, IForkChangedEvent, IForkCreationResponse, IForkManager } from './tokens';
5
5
  import { IForkProvider } from './ydrive';
@@ -32,10 +32,12 @@ export declare class ForkManager implements IForkManager {
32
32
  private _eventManager;
33
33
  private _forkAddedSignal;
34
34
  private _forkDeletedSignal;
35
+ private _serverSettings?;
35
36
  }
36
37
  export declare namespace ForkManager {
37
38
  interface IOptions {
38
39
  contentProvider: ICollaborativeContentProvider;
39
40
  eventManager: Event.IManager;
41
+ serverSettings?: ServerConnection.ISettings;
40
42
  }
41
43
  }
@@ -15,6 +15,7 @@ export class ForkManager {
15
15
  this._contentProvider = contentProvider;
16
16
  this._eventManager = eventManager;
17
17
  this._eventManager.stream.connect(this._handleEvent, this);
18
+ this._serverSettings = options.serverSettings;
18
19
  }
19
20
  get isDisposed() {
20
21
  return this._disposed;
@@ -40,13 +41,13 @@ export class ForkManager {
40
41
  body: JSON.stringify({ title, description, synchronize })
41
42
  };
42
43
  const url = URLExt.join(ROOM_FORK_URL, rootId);
43
- const response = await requestAPI(url, init);
44
+ const response = await requestAPI(url, init, this._serverSettings);
44
45
  return response;
45
46
  }
46
47
  async getAllForks(rootId) {
47
48
  const url = URLExt.join(ROOM_FORK_URL, rootId);
48
49
  const init = { method: 'GET' };
49
- const response = await requestAPI(url, init);
50
+ const response = await requestAPI(url, init, this._serverSettings);
50
51
  return response;
51
52
  }
52
53
  async deleteFork(options) {
@@ -54,7 +55,7 @@ export class ForkManager {
54
55
  const url = URLExt.join(ROOM_FORK_URL, forkId);
55
56
  const query = URLExt.objectToQueryString({ merge });
56
57
  const init = { method: 'DELETE' };
57
- await requestAPI(`${url}${query}`, init);
58
+ await requestAPI(`${url}${query}`, init, this._serverSettings);
58
59
  }
59
60
  getProvider(options) {
60
61
  const { documentPath, format, type } = options;
package/lib/requests.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Contents } from '@jupyterlab/services';
1
+ import { ServerConnection, Contents } from '@jupyterlab/services';
2
2
  export declare const ROOM_FORK_URL = "api/collaboration/fork";
3
3
  /**
4
4
  * Document session model
@@ -28,7 +28,7 @@ export interface ISessionModel {
28
28
  * @param init Initial values for the request
29
29
  * @returns The response body interpreted as JSON
30
30
  */
31
- export declare function requestAPI<T = any>(endPoint?: string, init?: RequestInit): Promise<T>;
32
- export declare function requestDocSession(format: string, type: string, path: string): Promise<ISessionModel>;
33
- export declare function requestDocumentTimeline(format: string, type: string, path: string): Promise<any>;
34
- export declare function requestUndoRedo(roomid: string, action: 'undo' | 'redo' | 'restore', steps: number, forkRoom: string): Promise<any>;
31
+ export declare function requestAPI<T = any>(endPoint?: string, init?: RequestInit, serverSettings?: ServerConnection.ISettings): Promise<T>;
32
+ export declare function requestDocSession(format: string, type: string, path: string, serverSettings?: ServerConnection.ISettings): Promise<ISessionModel>;
33
+ export declare function requestDocumentTimeline(format: string, type: string, path: string, serverSettings?: ServerConnection.ISettings): Promise<any>;
34
+ export declare function requestUndoRedo(roomid: string, action: 'undo' | 'redo' | 'restore', steps: number, forkRoom: string, serverSettings?: ServerConnection.ISettings): Promise<any>;
package/lib/requests.js CHANGED
@@ -19,9 +19,9 @@ export const ROOM_FORK_URL = 'api/collaboration/fork';
19
19
  * @param init Initial values for the request
20
20
  * @returns The response body interpreted as JSON
21
21
  */
22
- export async function requestAPI(endPoint = '', init = {}) {
22
+ export async function requestAPI(endPoint = '', init = {}, serverSettings) {
23
23
  // Make request to Jupyter API
24
- const settings = ServerConnection.makeSettings();
24
+ const settings = serverSettings !== null && serverSettings !== void 0 ? serverSettings : ServerConnection.makeSettings();
25
25
  const requestUrl = URLExt.join(settings.baseUrl, endPoint);
26
26
  let response;
27
27
  try {
@@ -44,8 +44,8 @@ export async function requestAPI(endPoint = '', init = {}) {
44
44
  }
45
45
  return data;
46
46
  }
47
- export async function requestDocSession(format, type, path) {
48
- const settings = ServerConnection.makeSettings();
47
+ export async function requestDocSession(format, type, path, serverSettings) {
48
+ const settings = serverSettings !== null && serverSettings !== void 0 ? serverSettings : ServerConnection.makeSettings();
49
49
  const url = URLExt.join(settings.baseUrl, DOC_SESSION_URL, encodeURIComponent(path));
50
50
  const body = {
51
51
  method: 'PUT',
@@ -72,8 +72,8 @@ export async function requestDocSession(format, type, path) {
72
72
  }
73
73
  return data;
74
74
  }
75
- export async function requestDocumentTimeline(format, type, path) {
76
- const settings = ServerConnection.makeSettings();
75
+ export async function requestDocumentTimeline(format, type, path, serverSettings) {
76
+ const settings = serverSettings !== null && serverSettings !== void 0 ? serverSettings : ServerConnection.makeSettings();
77
77
  let url = URLExt.join(settings.baseUrl, TIMELINE_URL, path);
78
78
  url = url.concat(`?format=${format}&&type=${type}`);
79
79
  const body = {
@@ -88,8 +88,8 @@ export async function requestDocumentTimeline(format, type, path) {
88
88
  }
89
89
  return response;
90
90
  }
91
- export async function requestUndoRedo(roomid, action, steps, forkRoom) {
92
- const settings = ServerConnection.makeSettings();
91
+ export async function requestUndoRedo(roomid, action, steps, forkRoom, serverSettings) {
92
+ const settings = serverSettings !== null && serverSettings !== void 0 ? serverSettings : ServerConnection.makeSettings();
93
93
  let url = URLExt.join(settings.baseUrl, DOC_FORK_URL, encodeURIComponent(roomid));
94
94
  url = url.concat(`?action=${action}&&steps=${steps}&&forkRoom=${forkRoom}`);
95
95
  const body = { method: 'PUT' };
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
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) Jupyter Development Team.
2
2
  // Distributed under the terms of the Modified BSD License.
3
- import { PageConfig, URLExt } from '@jupyterlab/coreutils';
3
+ import { PageConfig } from '@jupyterlab/coreutils';
4
4
  import { PromiseDelegate } from '@lumino/coreutils';
5
5
  import { Signal } from '@lumino/signaling';
6
6
  import { WebSocketProvider } from './yprovider';
@@ -8,10 +8,6 @@ import * as decoding from 'lib0/decoding';
8
8
  import * as encoding from 'lib0/encoding';
9
9
  const DISABLE_RTC = PageConfig.getOption('disableRTC') === 'true' ? true : false;
10
10
  const RAW_MESSAGE_TYPE = 2;
11
- /**
12
- * The url for the default drive service.
13
- */
14
- const DOCUMENT_PROVIDER_URL = 'api/collaboration/room';
15
11
  export class RtcContentProvider {
16
12
  constructor(options) {
17
13
  this._onCreate = (options, sharedModel) => {
@@ -30,13 +26,13 @@ export class RtcContentProvider {
30
26
  });
31
27
  try {
32
28
  const provider = new WebSocketProvider({
33
- url: URLExt.join(this._serverSettings.wsUrl, DOCUMENT_PROVIDER_URL),
34
29
  path: options.path,
35
30
  format: options.format,
36
31
  contentType: options.contentType,
37
32
  model: sharedModel,
38
33
  user: this._user,
39
- translator: this._trans
34
+ translator: this._trans,
35
+ serverSettings: this._serverSettings
40
36
  });
41
37
  // Add the document path in the list of opened ones for this user.
42
38
  const state = ((_e = this._globalAwareness) === null || _e === void 0 ? void 0 : _e.getLocalState()) || {};
@@ -195,6 +191,13 @@ export class RtcContentProvider {
195
191
  const key = `${options.format}:${options.type}:${localPath}`;
196
192
  const provider = this._providers.get(key);
197
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.
198
201
  // If the document doesn't exist, `super.get` will reject with an
199
202
  // error and the provider will never be resolved.
200
203
  // Use `Promise.all` to reject as soon as possible. The Context will
@@ -1,5 +1,5 @@
1
1
  import { IDocumentProvider } from '@jupyter/collaborative-drive';
2
- import { User } from '@jupyterlab/services';
2
+ import { ServerConnection, User } from '@jupyterlab/services';
3
3
  import { TranslationBundle } from '@jupyterlab/translation';
4
4
  import { DocumentChange, YDocument } from '@jupyter/ydoc';
5
5
  import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
@@ -32,23 +32,28 @@ export declare class WebSocketProvider implements IDocumentProvider, IForkProvid
32
32
  */
33
33
  dispose(): void;
34
34
  reconnect(): Promise<void>;
35
+ private get _serverUrl();
35
36
  private _connect;
36
37
  connectToForkDoc(forkRoomId: string, sessionId: string): Promise<void>;
37
38
  get wsProvider(): YWebsocketProvider | null;
38
39
  private _disconnect;
39
40
  private _onUserChanged;
41
+ private _buildSessionExpiredMessage;
40
42
  private _onConnectionClosed;
41
43
  private _onSync;
44
+ private _getCloseReasonMessage;
42
45
  private _awareness;
43
46
  private _contentType;
44
47
  private _format;
45
48
  private _isDisposed;
46
49
  private _path;
47
50
  private _ready;
48
- private _serverUrl;
51
+ private _customServerUrl?;
49
52
  private _sharedModel;
50
53
  private _yWebsocketProvider;
54
+ private _serverSettings;
51
55
  private _trans;
56
+ private _hasSynced;
52
57
  }
53
58
  /**
54
59
  * A namespace for WebSocketProvider statics.
@@ -61,7 +66,7 @@ export declare namespace WebSocketProvider {
61
66
  /**
62
67
  * The server URL
63
68
  */
64
- url: string;
69
+ url?: string;
65
70
  /**
66
71
  * The document file path
67
72
  */
@@ -86,5 +91,9 @@ export declare namespace WebSocketProvider {
86
91
  * The jupyterlab translator
87
92
  */
88
93
  translator: TranslationBundle;
94
+ /**
95
+ * The server settings.
96
+ */
97
+ serverSettings?: ServerConnection.ISettings;
89
98
  }
90
99
  }
package/lib/yprovider.js CHANGED
@@ -2,11 +2,17 @@
2
2
  | Copyright (c) Jupyter Development Team.
3
3
  | Distributed under the terms of the Modified BSD License.
4
4
  |----------------------------------------------------------------------------*/
5
- import { showErrorMessage, Dialog } from '@jupyterlab/apputils';
5
+ import { Dialog, showDialog } from '@jupyterlab/apputils';
6
+ import { ServerConnection } from '@jupyterlab/services';
6
7
  import { PromiseDelegate } from '@lumino/coreutils';
7
8
  import { Signal } from '@lumino/signaling';
8
9
  import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
9
10
  import { requestDocSession } from './requests';
11
+ import { URLExt } from '@jupyterlab/coreutils';
12
+ /**
13
+ * The url for the default drive service.
14
+ */
15
+ const DOCUMENT_PROVIDER_URL = 'api/collaboration/room';
10
16
  /**
11
17
  * A class to provide Yjs synchronization over WebSocket.
12
18
  *
@@ -20,12 +26,47 @@ export class WebSocketProvider {
20
26
  * @param options The instantiation options for a WebSocketProvider
21
27
  */
22
28
  constructor(options) {
23
- this._onConnectionClosed = (event) => {
29
+ var _a;
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
+ }
24
42
  if (event.code === 1003) {
25
43
  console.error('Document provider closed:', event.reason);
26
- showErrorMessage(this._trans.__('Document session error'), event.reason, [
27
- Dialog.okButton()
28
- ]);
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
+ }
29
70
  // Dispose shared model immediately. Better break the document model,
30
71
  // than overriding data on disk.
31
72
  this._sharedModel.dispose();
@@ -33,6 +74,7 @@ export class WebSocketProvider {
33
74
  };
34
75
  this._onSync = (isSynced) => {
35
76
  if (isSynced) {
77
+ this._hasSynced = true;
36
78
  if (this._yWebsocketProvider) {
37
79
  this._yWebsocketProvider.off('sync', this._onSync);
38
80
  const state = this._sharedModel.ydoc.getMap('state');
@@ -42,14 +84,17 @@ export class WebSocketProvider {
42
84
  }
43
85
  };
44
86
  this._ready = new PromiseDelegate();
87
+ this._hasSynced = false;
45
88
  this._isDisposed = false;
46
89
  this._path = options.path;
47
90
  this._contentType = options.contentType;
48
91
  this._format = options.format;
49
- this._serverUrl = options.url;
92
+ this._customServerUrl = options.url;
50
93
  this._sharedModel = options.model;
51
94
  this._awareness = options.model.awareness;
52
95
  this._yWebsocketProvider = null;
96
+ this._serverSettings =
97
+ (_a = options.serverSettings) !== null && _a !== void 0 ? _a : ServerConnection.makeSettings();
53
98
  this._trans = options.translator;
54
99
  const user = options.user;
55
100
  user.ready
@@ -97,22 +142,38 @@ export class WebSocketProvider {
97
142
  this._disconnect();
98
143
  this._connect();
99
144
  }
145
+ get _serverUrl() {
146
+ var _a;
147
+ return ((_a = this._customServerUrl) !== null && _a !== void 0 ? _a : URLExt.join(this._serverSettings.wsUrl, DOCUMENT_PROVIDER_URL));
148
+ }
100
149
  async _connect() {
101
- const session = await requestDocSession(this._format, this._contentType, this._path);
150
+ const session = await requestDocSession(this._format, this._contentType, this._path, this._serverSettings);
151
+ const token = this._serverSettings.token;
152
+ const params = { sessionId: session.sessionId };
153
+ if (this._serverSettings.appendToken && token !== '') {
154
+ params['token'] = token;
155
+ }
102
156
  this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, `${session.format}:${session.type}:${session.fileId}`, this._sharedModel.ydoc, {
103
157
  disableBc: true,
104
- params: { sessionId: session.sessionId },
105
- awareness: this._awareness
158
+ params,
159
+ awareness: this._awareness,
160
+ WebSocketPolyfill: this._serverSettings.WebSocket
106
161
  });
107
162
  this._yWebsocketProvider.on('sync', this._onSync);
108
163
  this._yWebsocketProvider.on('connection-close', this._onConnectionClosed);
109
164
  }
110
165
  async connectToForkDoc(forkRoomId, sessionId) {
166
+ const token = this._serverSettings.token;
167
+ const params = { sessionId };
168
+ if (this._serverSettings.appendToken && token !== '') {
169
+ params['token'] = token;
170
+ }
111
171
  this._disconnect();
112
172
  this._yWebsocketProvider = new YWebsocketProvider(this._serverUrl, forkRoomId, this._sharedModel.ydoc, {
113
173
  disableBc: true,
114
- params: { sessionId },
115
- awareness: this._awareness
174
+ params,
175
+ awareness: this._awareness,
176
+ WebSocketPolyfill: this._serverSettings.WebSocket
116
177
  });
117
178
  }
118
179
  get wsProvider() {
@@ -128,4 +189,39 @@ export class WebSocketProvider {
128
189
  _onUserChanged(user) {
129
190
  this._awareness.setLocalStateField('user', user.identity);
130
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
+ }
131
227
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jupyter/docprovider",
3
- "version": "4.2.0",
3
+ "version": "4.3.0-alpha.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.2.0",
44
+ "@jupyter/collaborative-drive": "^4.3.0-alpha.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",