@jupyter/docprovider 4.3.0 → 4.4.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/awareness.d.ts +5 -5
- package/lib/awareness.js +8 -8
- package/lib/tokens.d.ts +117 -0
- package/lib/tokens.js +8 -0
- package/lib/ydrive.d.ts +15 -3
- package/lib/ydrive.js +50 -73
- package/lib/yprovider.d.ts +4 -2
- package/lib/yprovider.js +74 -9
- package/package.json +4 -3
package/lib/awareness.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { IDisposable } from '@lumino/disposable';
|
|
1
|
+
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
3
2
|
import { IAwareness } from '@jupyter/ydoc';
|
|
4
|
-
import {
|
|
3
|
+
import { User } from '@jupyterlab/services';
|
|
4
|
+
import { IAwarenessProvider } from './tokens';
|
|
5
5
|
export interface IContent {
|
|
6
6
|
type: string;
|
|
7
7
|
body: string;
|
|
@@ -12,7 +12,7 @@ export interface IContent {
|
|
|
12
12
|
* We specify custom messages that the server can interpret. For reference please look in yjs_ws_server.
|
|
13
13
|
*
|
|
14
14
|
*/
|
|
15
|
-
export declare class WebSocketAwarenessProvider extends
|
|
15
|
+
export declare class WebSocketAwarenessProvider extends YWebsocketProvider implements IAwarenessProvider {
|
|
16
16
|
/**
|
|
17
17
|
* Construct a new WebSocketAwarenessProvider
|
|
18
18
|
*
|
|
@@ -22,9 +22,9 @@ export declare class WebSocketAwarenessProvider extends WebsocketProvider implem
|
|
|
22
22
|
get isDisposed(): boolean;
|
|
23
23
|
dispose(): void;
|
|
24
24
|
private _onUserChanged;
|
|
25
|
+
readonly awareness: IAwareness;
|
|
25
26
|
private _isDisposed;
|
|
26
27
|
private _user;
|
|
27
|
-
private _awareness;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* A namespace for WebSocketAwarenessProvider statics.
|
package/lib/awareness.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { WebsocketProvider } from 'y-websocket';
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
6
6
|
/**
|
|
7
7
|
* A class to provide Yjs synchronization over WebSocket.
|
|
8
8
|
*
|
|
9
9
|
* We specify custom messages that the server can interpret. For reference please look in yjs_ws_server.
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
|
-
export class WebSocketAwarenessProvider extends
|
|
12
|
+
export class WebSocketAwarenessProvider extends YWebsocketProvider {
|
|
13
13
|
/**
|
|
14
14
|
* Construct a new WebSocketAwarenessProvider
|
|
15
15
|
*
|
|
@@ -20,7 +20,7 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
|
|
|
20
20
|
awareness: options.awareness
|
|
21
21
|
});
|
|
22
22
|
this._isDisposed = false;
|
|
23
|
-
this.
|
|
23
|
+
this.awareness = options.awareness;
|
|
24
24
|
this._user = options.user;
|
|
25
25
|
this._user.ready
|
|
26
26
|
.then(() => this._onUserChanged(this._user))
|
|
@@ -39,6 +39,6 @@ export class WebSocketAwarenessProvider extends WebsocketProvider {
|
|
|
39
39
|
this.destroy();
|
|
40
40
|
}
|
|
41
41
|
_onUserChanged(user) {
|
|
42
|
-
this.
|
|
42
|
+
this.awareness.setLocalStateField('user', user.identity);
|
|
43
43
|
}
|
|
44
44
|
}
|
package/lib/tokens.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { TranslationBundle } from '@jupyterlab/translation';
|
|
2
|
+
import { ServerConnection, User, Contents } from '@jupyterlab/services';
|
|
3
|
+
import { DocumentChange, YDocument, IAwareness } from '@jupyter/ydoc';
|
|
4
|
+
import { IDocumentProvider } from '@jupyter/collaborative-drive';
|
|
1
5
|
import { Token } from '@lumino/coreutils';
|
|
2
6
|
import { IDisposable } from '@lumino/disposable';
|
|
3
7
|
import { ISignal } from '@lumino/signaling';
|
|
@@ -101,6 +105,119 @@ export interface IForkManager extends IDisposable {
|
|
|
101
105
|
* Token providing a fork manager instance.
|
|
102
106
|
*/
|
|
103
107
|
export declare const IForkManagerToken: Token<IForkManager>;
|
|
108
|
+
/**
|
|
109
|
+
* A factory for creating document providers.
|
|
110
|
+
*/
|
|
111
|
+
export interface IDocumentProviderFactory {
|
|
112
|
+
/**
|
|
113
|
+
* Create a new document provider.
|
|
114
|
+
*
|
|
115
|
+
* @param options - The instantiation options for a document provider.
|
|
116
|
+
* @returns The document provider.
|
|
117
|
+
*/
|
|
118
|
+
create(options: IDocumentProviderFactory.IOptions): IDocumentProvider & IForkProvider;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A namespace for IDocumentProviderFactory.
|
|
122
|
+
*/
|
|
123
|
+
export declare namespace IDocumentProviderFactory {
|
|
124
|
+
/**
|
|
125
|
+
* The instantiation options for a document provider.
|
|
126
|
+
*/
|
|
127
|
+
interface IOptions {
|
|
128
|
+
/**
|
|
129
|
+
* The server URL
|
|
130
|
+
*/
|
|
131
|
+
url?: string;
|
|
132
|
+
/**
|
|
133
|
+
* The document file path
|
|
134
|
+
*/
|
|
135
|
+
path: string;
|
|
136
|
+
/**
|
|
137
|
+
* Content type
|
|
138
|
+
*/
|
|
139
|
+
contentType: string;
|
|
140
|
+
/**
|
|
141
|
+
* The source format
|
|
142
|
+
*/
|
|
143
|
+
format: string;
|
|
144
|
+
/**
|
|
145
|
+
* The shared model
|
|
146
|
+
*/
|
|
147
|
+
model: YDocument<DocumentChange>;
|
|
148
|
+
/**
|
|
149
|
+
* The user data
|
|
150
|
+
*/
|
|
151
|
+
user: User.IManager;
|
|
152
|
+
/**
|
|
153
|
+
* The jupyterlab translator
|
|
154
|
+
*/
|
|
155
|
+
translator: TranslationBundle;
|
|
156
|
+
/**
|
|
157
|
+
* The server settings.
|
|
158
|
+
*/
|
|
159
|
+
serverSettings?: ServerConnection.ISettings;
|
|
160
|
+
/**
|
|
161
|
+
* The drive to use for loading and saving document content.
|
|
162
|
+
*/
|
|
163
|
+
drive: Contents.IDrive;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Token providing a document provider factory instance.
|
|
168
|
+
*/
|
|
169
|
+
export declare const IDocumentProviderFactory: Token<IDocumentProviderFactory>;
|
|
170
|
+
/**
|
|
171
|
+
* An interface for an awareness provider.
|
|
172
|
+
*/
|
|
173
|
+
export interface IAwarenessProvider extends IDisposable {
|
|
174
|
+
/**
|
|
175
|
+
* The awareness object.
|
|
176
|
+
*/
|
|
177
|
+
readonly awareness: IAwareness;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* A factory for creating awareness providers.
|
|
181
|
+
*/
|
|
182
|
+
export interface IAwarenessProviderFactory {
|
|
183
|
+
/**
|
|
184
|
+
* Create a new awareness provider.
|
|
185
|
+
*
|
|
186
|
+
* @param options - The instantiation options for an awareness provider.
|
|
187
|
+
* @returns The awareness provider.
|
|
188
|
+
*/
|
|
189
|
+
create(options: IAwarenessProviderFactory.IOptions): IAwarenessProvider;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* A namespace for IAwarenessProviderFactory.
|
|
193
|
+
*/
|
|
194
|
+
export declare namespace IAwarenessProviderFactory {
|
|
195
|
+
/**
|
|
196
|
+
* The instantiation options for an awareness provider.
|
|
197
|
+
*/
|
|
198
|
+
interface IOptions {
|
|
199
|
+
/**
|
|
200
|
+
* The room ID
|
|
201
|
+
*/
|
|
202
|
+
roomID: string;
|
|
203
|
+
/**
|
|
204
|
+
* The awareness object
|
|
205
|
+
*/
|
|
206
|
+
awareness: IAwareness;
|
|
207
|
+
/**
|
|
208
|
+
* The user data
|
|
209
|
+
*/
|
|
210
|
+
user: User.IManager;
|
|
211
|
+
/**
|
|
212
|
+
* The server settings.
|
|
213
|
+
*/
|
|
214
|
+
serverSettings: ServerConnection.ISettings;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Token providing an awareness provider factory instance.
|
|
219
|
+
*/
|
|
220
|
+
export declare const IAwarenessProviderFactory: Token<IAwarenessProviderFactory>;
|
|
104
221
|
export interface ISessionClosePayload {
|
|
105
222
|
reason: 'unknown_session' | 'version_mismatch' | 'initialization_error';
|
|
106
223
|
sessionId?: string;
|
package/lib/tokens.js
CHANGED
|
@@ -7,3 +7,11 @@ import { Token } from '@lumino/coreutils';
|
|
|
7
7
|
* Token providing a fork manager instance.
|
|
8
8
|
*/
|
|
9
9
|
export const IForkManagerToken = new Token('@jupyter/docprovider:IForkManagerToken');
|
|
10
|
+
/**
|
|
11
|
+
* Token providing a document provider factory instance.
|
|
12
|
+
*/
|
|
13
|
+
export const IDocumentProviderFactory = new Token('@jupyter/docprovider:IDocumentProviderFactory');
|
|
14
|
+
/**
|
|
15
|
+
* Token providing an awareness provider factory instance.
|
|
16
|
+
*/
|
|
17
|
+
export const IAwarenessProviderFactory = new Token('@jupyter/docprovider:IAwarenessProviderFactory');
|
package/lib/ydrive.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
2
|
+
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
1
3
|
import { TranslationBundle } from '@jupyterlab/translation';
|
|
2
4
|
import { Contents, IContentProvider, ServerConnection, User } from '@jupyterlab/services';
|
|
3
5
|
import { ISignal } from '@lumino/signaling';
|
|
4
6
|
import { IDocumentProvider, ISharedModelFactory } from '@jupyter/collaborative-drive';
|
|
7
|
+
import { IDocumentProviderFactory } from './tokens';
|
|
5
8
|
import { Awareness } from 'y-protocols/awareness';
|
|
6
|
-
import { ISettingRegistry } from '@jupyterlab/settingregistry';
|
|
7
9
|
export interface IForkProvider {
|
|
8
10
|
connectToForkDoc: (forkRoomId: string, sessionId: string) => Promise<void>;
|
|
9
11
|
reconnect: () => Promise<void>;
|
|
10
12
|
contentType: string;
|
|
11
13
|
format: string;
|
|
14
|
+
save?: () => Promise<void>;
|
|
12
15
|
}
|
|
13
16
|
declare namespace RtcContentProvider {
|
|
14
17
|
interface IOptions {
|
|
@@ -16,9 +19,17 @@ declare namespace RtcContentProvider {
|
|
|
16
19
|
trans: TranslationBundle;
|
|
17
20
|
globalAwareness: Awareness | null;
|
|
18
21
|
serverSettings: ServerConnection.ISettings;
|
|
19
|
-
|
|
22
|
+
documentManager?: IDocumentManager | null;
|
|
20
23
|
currentDrive: Contents.IDrive;
|
|
21
24
|
fileChanged?: ISignal<Contents.IDrive, Contents.IChangedArgs>;
|
|
25
|
+
providerFactory: IDocumentProviderFactory;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Pass `documentManager` instead. Used as a fallback when
|
|
28
|
+
* `documentManager` is not provided. Will be removed in a future major
|
|
29
|
+
* release.
|
|
30
|
+
* @see {@link IOptions.documentManager}
|
|
31
|
+
*/
|
|
32
|
+
docmanagerSettings?: ISettingRegistry.ISettings | null;
|
|
22
33
|
}
|
|
23
34
|
}
|
|
24
35
|
export declare class RtcContentProvider implements IContentProvider {
|
|
@@ -55,7 +66,6 @@ export declare class RtcContentProvider implements IContentProvider {
|
|
|
55
66
|
get fileChanged(): ISignal<this, Contents.IChangedArgs>;
|
|
56
67
|
private _onCreate;
|
|
57
68
|
private _user;
|
|
58
|
-
private _saveCounter;
|
|
59
69
|
private _currentDrive;
|
|
60
70
|
private _trans;
|
|
61
71
|
private _globalAwareness;
|
|
@@ -63,6 +73,8 @@ export declare class RtcContentProvider implements IContentProvider {
|
|
|
63
73
|
private _providerFileChanged;
|
|
64
74
|
private _driveFileChanged?;
|
|
65
75
|
private _serverSettings;
|
|
76
|
+
private _documentManager;
|
|
66
77
|
private _docmanagerSettings;
|
|
78
|
+
private _providerFactory;
|
|
67
79
|
}
|
|
68
80
|
export {};
|
package/lib/ydrive.js
CHANGED
|
@@ -1,45 +1,67 @@
|
|
|
1
1
|
// Copyright (c) Jupyter Development Team.
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
3
|
import { PageConfig } from '@jupyterlab/coreutils';
|
|
4
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
5
4
|
import { Signal } from '@lumino/signaling';
|
|
6
|
-
import { WebSocketProvider } from './yprovider';
|
|
7
|
-
import * as decoding from 'lib0/decoding';
|
|
8
|
-
import * as encoding from 'lib0/encoding';
|
|
9
5
|
const DISABLE_RTC = PageConfig.getOption('disableRTC') === 'true' ? true : false;
|
|
10
|
-
const RAW_MESSAGE_TYPE = 2;
|
|
11
6
|
export class RtcContentProvider {
|
|
12
7
|
constructor(options) {
|
|
8
|
+
var _a, _b;
|
|
13
9
|
this._onCreate = (options, sharedModel) => {
|
|
14
|
-
var _a, _b, _c
|
|
10
|
+
var _a, _b, _c;
|
|
15
11
|
if (typeof options.format !== 'string') {
|
|
16
12
|
return;
|
|
17
13
|
}
|
|
18
14
|
// Set initial autosave value, used to determine backend autosave (default: true)
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
const getAutosave = () => {
|
|
16
|
+
var _a, _b, _c, _d;
|
|
17
|
+
if (this._documentManager) {
|
|
18
|
+
return (_a = this._documentManager.autosave) !== null && _a !== void 0 ? _a : true;
|
|
19
|
+
}
|
|
20
|
+
return ((_d = (_c = (_b = this._docmanagerSettings) === null || _b === void 0 ? void 0 : _b.composite) === null || _c === void 0 ? void 0 : _c['autosave']) !== null && _d !== void 0 ? _d : true);
|
|
21
|
+
};
|
|
22
|
+
sharedModel.awareness.setLocalStateField('autosave', getAutosave());
|
|
23
|
+
if (this._documentManager) {
|
|
24
|
+
// Watch for autosave changes on the document manager.
|
|
25
|
+
const handleStateChanged = (_, args) => {
|
|
26
|
+
if (args.name === 'autosave') {
|
|
27
|
+
sharedModel.awareness.setLocalStateField('autosave', getAutosave());
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
this._documentManager.stateChanged.connect(handleStateChanged);
|
|
31
|
+
sharedModel.disposed.connect(() => {
|
|
32
|
+
var _a;
|
|
33
|
+
(_a = this._documentManager) === null || _a === void 0 ? void 0 : _a.stateChanged.disconnect(handleStateChanged);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if (this._docmanagerSettings) {
|
|
37
|
+
// Fall back to watching the deprecated docmanager settings.
|
|
38
|
+
const handleSettingsChanged = () => {
|
|
39
|
+
sharedModel.awareness.setLocalStateField('autosave', getAutosave());
|
|
40
|
+
};
|
|
41
|
+
this._docmanagerSettings.changed.connect(handleSettingsChanged);
|
|
42
|
+
sharedModel.disposed.connect(() => {
|
|
43
|
+
var _a;
|
|
44
|
+
(_a = this._docmanagerSettings) === null || _a === void 0 ? void 0 : _a.changed.disconnect(handleSettingsChanged);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
27
47
|
try {
|
|
28
|
-
const
|
|
48
|
+
const providerOptions = {
|
|
29
49
|
path: options.path,
|
|
30
50
|
format: options.format,
|
|
31
51
|
contentType: options.contentType,
|
|
32
52
|
model: sharedModel,
|
|
33
53
|
user: this._user,
|
|
34
54
|
translator: this._trans,
|
|
35
|
-
serverSettings: this._serverSettings
|
|
36
|
-
|
|
55
|
+
serverSettings: this._serverSettings,
|
|
56
|
+
drive: this._currentDrive
|
|
57
|
+
};
|
|
58
|
+
const provider = this._providerFactory.create(providerOptions);
|
|
37
59
|
// Add the document path in the list of opened ones for this user.
|
|
38
|
-
const state = ((
|
|
60
|
+
const state = ((_a = this._globalAwareness) === null || _a === void 0 ? void 0 : _a.getLocalState()) || {};
|
|
39
61
|
const documents = state.documents || [];
|
|
40
62
|
if (!documents.includes(options.path)) {
|
|
41
63
|
documents.push(options.path);
|
|
42
|
-
(
|
|
64
|
+
(_b = this._globalAwareness) === null || _b === void 0 ? void 0 : _b.setLocalStateField('documents', documents);
|
|
43
65
|
}
|
|
44
66
|
let path = options.path;
|
|
45
67
|
let key = `${options.format}:${options.contentType}:${path}`;
|
|
@@ -105,7 +127,7 @@ export class RtcContentProvider {
|
|
|
105
127
|
name: 'path'
|
|
106
128
|
});
|
|
107
129
|
};
|
|
108
|
-
(
|
|
130
|
+
(_c = this._driveFileChanged) === null || _c === void 0 ? void 0 : _c.connect(handleFileChangedSignal);
|
|
109
131
|
sharedModel.changed.connect(async (_, change) => {
|
|
110
132
|
var _a;
|
|
111
133
|
if (!change.stateChange) {
|
|
@@ -161,7 +183,6 @@ export class RtcContentProvider {
|
|
|
161
183
|
console.error(`Failed to open websocket connection for ${options.path}.\n:${error}`);
|
|
162
184
|
}
|
|
163
185
|
};
|
|
164
|
-
this._saveCounter = 0;
|
|
165
186
|
// This is for emitting signals to be proxied to `Drive.fileChanged`
|
|
166
187
|
this._providerFileChanged = new Signal(this);
|
|
167
188
|
this._user = options.user;
|
|
@@ -171,8 +192,10 @@ export class RtcContentProvider {
|
|
|
171
192
|
this._currentDrive = options.currentDrive;
|
|
172
193
|
this.sharedModelFactory = new SharedModelFactory(this._onCreate);
|
|
173
194
|
this._providers = new Map();
|
|
174
|
-
this.
|
|
195
|
+
this._documentManager = (_a = options.documentManager) !== null && _a !== void 0 ? _a : null;
|
|
196
|
+
this._docmanagerSettings = (_b = options.docmanagerSettings) !== null && _b !== void 0 ? _b : null;
|
|
175
197
|
this._driveFileChanged = options.fileChanged;
|
|
198
|
+
this._providerFactory = options.providerFactory;
|
|
176
199
|
}
|
|
177
200
|
get providers() {
|
|
178
201
|
return this._providers;
|
|
@@ -233,62 +256,13 @@ export class RtcContentProvider {
|
|
|
233
256
|
* file is saved.
|
|
234
257
|
*/
|
|
235
258
|
async save(localPath, options = {}) {
|
|
236
|
-
var _a;
|
|
237
259
|
// Check that there is a provider - it won't e.g. if the document model is not collaborative.
|
|
238
260
|
if (options.format && options.type) {
|
|
239
261
|
const key = `${options.format}:${options.type}:${localPath}`;
|
|
240
262
|
const provider = this._providers.get(key);
|
|
241
|
-
const saveId = ++this._saveCounter;
|
|
242
263
|
if (provider) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const delegate = new PromiseDelegate();
|
|
246
|
-
const handler = (event) => {
|
|
247
|
-
const data = new Uint8Array(event.data);
|
|
248
|
-
const decoder = decoding.createDecoder(data);
|
|
249
|
-
try {
|
|
250
|
-
const messageType = decoding.readVarUint(decoder);
|
|
251
|
-
if (messageType !== RAW_MESSAGE_TYPE) {
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
catch (_a) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
const rawReply = decoding.readVarString(decoder);
|
|
259
|
-
let reply = null;
|
|
260
|
-
try {
|
|
261
|
-
reply = JSON.parse(rawReply);
|
|
262
|
-
}
|
|
263
|
-
catch (e) {
|
|
264
|
-
console.debug('The raw reply received was not a JSON reply');
|
|
265
|
-
}
|
|
266
|
-
if (reply &&
|
|
267
|
-
reply['type'] === 'save' &&
|
|
268
|
-
reply['responseTo'] === saveId) {
|
|
269
|
-
if (reply.status === 'success') {
|
|
270
|
-
delegate.resolve();
|
|
271
|
-
}
|
|
272
|
-
else if (reply.status === 'failed') {
|
|
273
|
-
delegate.reject('Saving failed');
|
|
274
|
-
}
|
|
275
|
-
else if (reply.status === 'skipped') {
|
|
276
|
-
delegate.reject('Saving already in progress');
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
delegate.reject('Unrecognised save reply status');
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
ws.addEventListener('message', handler);
|
|
284
|
-
const encoder = encoding.createEncoder();
|
|
285
|
-
encoding.writeVarUint(encoder, RAW_MESSAGE_TYPE);
|
|
286
|
-
encoding.writeVarString(encoder, 'save');
|
|
287
|
-
encoding.writeVarUint(encoder, saveId);
|
|
288
|
-
const saveMessage = encoding.toUint8Array(encoder);
|
|
289
|
-
ws.send(saveMessage);
|
|
290
|
-
await delegate.promise;
|
|
291
|
-
ws.removeEventListener('message', handler);
|
|
264
|
+
if (provider.save) {
|
|
265
|
+
await provider.save();
|
|
292
266
|
}
|
|
293
267
|
const fetchOptions = {
|
|
294
268
|
type: options.type,
|
|
@@ -359,6 +333,9 @@ class SharedModelFactory {
|
|
|
359
333
|
}
|
|
360
334
|
if (this.documentFactories.has(options.contentType)) {
|
|
361
335
|
const factory = this.documentFactories.get(options.contentType);
|
|
336
|
+
if (!factory) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
362
339
|
const sharedModel = factory(options);
|
|
363
340
|
this._onCreate(options, sharedModel);
|
|
364
341
|
return sharedModel;
|
package/lib/yprovider.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
2
|
+
import { DocumentChange, YDocument } from '@jupyter/ydoc';
|
|
1
3
|
import { IDocumentProvider } from '@jupyter/collaborative-drive';
|
|
2
4
|
import { ServerConnection, User } from '@jupyterlab/services';
|
|
3
5
|
import { TranslationBundle } from '@jupyterlab/translation';
|
|
4
|
-
import { DocumentChange, YDocument } from '@jupyter/ydoc';
|
|
5
|
-
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
6
6
|
import { IForkProvider } from './ydrive';
|
|
7
7
|
/**
|
|
8
8
|
* A class to provide Yjs synchronization over WebSocket.
|
|
@@ -32,6 +32,7 @@ export declare class WebSocketProvider implements IDocumentProvider, IForkProvid
|
|
|
32
32
|
*/
|
|
33
33
|
dispose(): void;
|
|
34
34
|
reconnect(): Promise<void>;
|
|
35
|
+
save(): Promise<void>;
|
|
35
36
|
private get _serverUrl();
|
|
36
37
|
private _connect;
|
|
37
38
|
connectToForkDoc(forkRoomId: string, sessionId: string): Promise<void>;
|
|
@@ -54,6 +55,7 @@ export declare class WebSocketProvider implements IDocumentProvider, IForkProvid
|
|
|
54
55
|
private _serverSettings;
|
|
55
56
|
private _trans;
|
|
56
57
|
private _hasSynced;
|
|
58
|
+
private _saveCounter;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* A namespace for WebSocketProvider statics.
|
package/lib/yprovider.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
6
|
-
import { ServerConnection } from '@jupyterlab/services';
|
|
7
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
8
|
-
import { Signal } from '@lumino/signaling';
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
9
5
|
import { WebsocketProvider as YWebsocketProvider } from 'y-websocket';
|
|
10
|
-
import
|
|
6
|
+
import * as decoding from 'lib0/decoding';
|
|
7
|
+
import * as encoding from 'lib0/encoding';
|
|
8
|
+
import { Signal } from '@lumino/signaling';
|
|
9
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
10
|
+
import { ServerConnection } from '@jupyterlab/services';
|
|
11
11
|
import { URLExt } from '@jupyterlab/coreutils';
|
|
12
|
+
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
13
|
+
import { requestDocSession } from './requests';
|
|
12
14
|
/**
|
|
13
15
|
* The url for the default drive service.
|
|
14
16
|
*/
|
|
15
17
|
const DOCUMENT_PROVIDER_URL = 'api/collaboration/room';
|
|
18
|
+
/**
|
|
19
|
+
* The raw message type.
|
|
20
|
+
*/
|
|
21
|
+
const RAW_MESSAGE_TYPE = 2;
|
|
16
22
|
/**
|
|
17
23
|
* A class to provide Yjs synchronization over WebSocket.
|
|
18
24
|
*
|
|
@@ -85,6 +91,7 @@ export class WebSocketProvider {
|
|
|
85
91
|
};
|
|
86
92
|
this._ready = new PromiseDelegate();
|
|
87
93
|
this._hasSynced = false;
|
|
94
|
+
this._saveCounter = 0;
|
|
88
95
|
this._isDisposed = false;
|
|
89
96
|
this._path = options.path;
|
|
90
97
|
this._contentType = options.contentType;
|
|
@@ -142,6 +149,64 @@ export class WebSocketProvider {
|
|
|
142
149
|
this._disconnect();
|
|
143
150
|
this._connect();
|
|
144
151
|
}
|
|
152
|
+
async save() {
|
|
153
|
+
var _a;
|
|
154
|
+
const ws = (_a = this._yWebsocketProvider) === null || _a === void 0 ? void 0 : _a.ws;
|
|
155
|
+
if (ws) {
|
|
156
|
+
const saveId = ++this._saveCounter;
|
|
157
|
+
const delegate = new PromiseDelegate();
|
|
158
|
+
const handler = (event) => {
|
|
159
|
+
const data = new Uint8Array(event.data);
|
|
160
|
+
const decoder = decoding.createDecoder(data);
|
|
161
|
+
try {
|
|
162
|
+
const messageType = decoding.readVarUint(decoder);
|
|
163
|
+
if (messageType !== RAW_MESSAGE_TYPE) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (_a) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const rawReply = decoding.readVarString(decoder);
|
|
171
|
+
let reply = null;
|
|
172
|
+
try {
|
|
173
|
+
reply = JSON.parse(rawReply);
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
console.debug('The raw reply received was not a JSON reply');
|
|
177
|
+
}
|
|
178
|
+
if (reply &&
|
|
179
|
+
reply['type'] === 'save' &&
|
|
180
|
+
reply['responseTo'] === saveId) {
|
|
181
|
+
if (reply.status === 'success') {
|
|
182
|
+
delegate.resolve();
|
|
183
|
+
}
|
|
184
|
+
else if (reply.status === 'failed') {
|
|
185
|
+
delegate.reject('Saving failed');
|
|
186
|
+
}
|
|
187
|
+
else if (reply.status === 'skipped') {
|
|
188
|
+
delegate.reject('Saving already in progress');
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
delegate.reject('Unrecognised save reply status');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
ws.addEventListener('message', handler);
|
|
196
|
+
const encoder = encoding.createEncoder();
|
|
197
|
+
encoding.writeVarUint(encoder, RAW_MESSAGE_TYPE);
|
|
198
|
+
encoding.writeVarString(encoder, 'save');
|
|
199
|
+
encoding.writeVarUint(encoder, saveId);
|
|
200
|
+
const saveMessage = encoding.toUint8Array(encoder);
|
|
201
|
+
ws.send(saveMessage);
|
|
202
|
+
try {
|
|
203
|
+
await delegate.promise;
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
ws.removeEventListener('message', handler);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
145
210
|
get _serverUrl() {
|
|
146
211
|
var _a;
|
|
147
212
|
return ((_a = this._customServerUrl) !== null && _a !== void 0 ? _a : URLExt.join(this._serverSettings.wsUrl, DOCUMENT_PROVIDER_URL));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyter/docprovider",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0-beta.0",
|
|
4
4
|
"description": "JupyterLab - Document Provider",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyter-collaboration",
|
|
6
6
|
"bugs": {
|
|
@@ -41,11 +41,12 @@
|
|
|
41
41
|
"watch": "tsc -b --watch"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@jupyter/collaborative-drive": "^4.
|
|
44
|
+
"@jupyter/collaborative-drive": "^4.4.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",
|
|
48
48
|
"@jupyterlab/coreutils": "^6.5.0",
|
|
49
|
+
"@jupyterlab/docmanager": "^4.5.0",
|
|
49
50
|
"@jupyterlab/notebook": "^4.5.0",
|
|
50
51
|
"@jupyterlab/services": "^7.5.0",
|
|
51
52
|
"@jupyterlab/translation": "^4.5.0",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"@types/jest": "^29.2.0",
|
|
64
65
|
"jest": "^29.5.0",
|
|
65
66
|
"rimraf": "^4.1.2",
|
|
66
|
-
"typescript": "~5.
|
|
67
|
+
"typescript": "~5.9.3"
|
|
67
68
|
},
|
|
68
69
|
"publishConfig": {
|
|
69
70
|
"access": "public"
|