jupyterpack 0.3.0 → 0.5.1
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/README.md +1 -1
- package/lib/document/commands.d.ts +8 -0
- package/lib/document/commands.js +76 -0
- package/lib/document/iframePanel.d.ts +4 -1
- package/lib/document/plugin.d.ts +2 -1
- package/lib/document/plugin.js +22 -4
- package/lib/document/toolbar.d.ts +9 -0
- package/lib/document/toolbar.js +20 -0
- package/lib/document/widgetFactory.d.ts +2 -1
- package/lib/document/widgetFactory.js +14 -6
- package/lib/index.d.ts +1 -1
- package/lib/pythonServer/baseServer.d.ts +82 -0
- package/lib/pythonServer/baseServer.js +141 -0
- package/lib/pythonServer/dash/dashServer.d.ts +5 -17
- package/lib/pythonServer/dash/dashServer.js +19 -24
- package/lib/pythonServer/index.d.ts +3 -3
- package/lib/pythonServer/index.js +5 -1
- package/lib/pythonServer/kernelExecutor.d.ts +2 -53
- package/lib/pythonServer/kernelExecutor.js +5 -70
- package/lib/pythonServer/shiny/shinyServer.d.ts +14 -0
- package/lib/pythonServer/shiny/shinyServer.js +49 -0
- package/lib/pythonServer/starlette/starletteServer.d.ts +13 -0
- package/lib/pythonServer/starlette/starletteServer.js +49 -0
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +6 -26
- package/lib/pythonServer/streamlit/streamlitServer.js +33 -41
- package/lib/pythonServer/tornado/tornadoServer.d.ts +6 -26
- package/lib/pythonServer/tornado/tornadoServer.js +28 -36
- package/lib/pythonWidget/comm.d.ts +11 -0
- package/lib/pythonWidget/comm.js +52 -0
- package/lib/pythonWidget/pythonWidget.d.ts +5 -0
- package/lib/pythonWidget/pythonWidget.js +19 -0
- package/lib/pythonWidget/pythonWidgetModel.d.ts +16 -4
- package/lib/pythonWidget/pythonWidgetModel.js +77 -13
- package/lib/sandpackWidget/sandpackFilesModel.d.ts +6 -2
- package/lib/sandpackWidget/sandpackFilesModel.js +13 -2
- package/lib/sandpackWidget/sandpackPanel.d.ts +10 -1
- package/lib/sandpackWidget/sandpackPanel.js +38 -3
- package/lib/swConnection/mainConnectionManager.d.ts +4 -4
- package/lib/swConnection/mainConnectionManager.js +23 -12
- package/lib/token.d.ts +2 -1
- package/lib/token.js +1 -0
- package/lib/tools.d.ts +6 -0
- package/lib/tools.js +25 -0
- package/lib/type.d.ts +39 -4
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.js +5 -1
- package/package.json +6 -6
- package/src/document/commands.ts +91 -0
- package/src/document/iframePanel.ts +4 -1
- package/src/document/plugin.ts +28 -7
- package/src/document/toolbar.ts +39 -0
- package/src/document/widgetFactory.ts +16 -6
- package/src/global.d.ts +5 -0
- package/src/pythonServer/baseServer.ts +245 -0
- package/src/pythonServer/dash/dashServer.ts +25 -35
- package/src/pythonServer/index.ts +9 -5
- package/src/pythonServer/kernelExecutor.ts +8 -147
- package/src/pythonServer/shiny/shinyServer.ts +62 -0
- package/src/pythonServer/starlette/starletteServer.ts +59 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +40 -62
- package/src/pythonServer/tornado/tornadoServer.ts +33 -60
- package/src/pythonWidget/comm.ts +65 -0
- package/src/pythonWidget/pythonWidget.ts +19 -1
- package/src/pythonWidget/pythonWidgetModel.ts +107 -20
- package/src/sandpackWidget/sandpackFilesModel.ts +17 -3
- package/src/sandpackWidget/sandpackPanel.ts +45 -4
- package/src/swConnection/mainConnectionManager.ts +28 -20
- package/src/token.ts +5 -1
- package/src/tools.ts +31 -0
- package/src/type.ts +46 -7
- package/src/websocket/websocket.ts +9 -1
- package/style/base.css +7 -0
- package/style/icons/autoreload.svg +16 -0
- package/style/icons/box.svg +12 -0
- package/style/icons/externallink.svg +10 -0
- package/lib/pythonServer/common/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/common/generatedPythonFiles.js +0 -72
- package/lib/pythonServer/dash/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/dash/generatedPythonFiles.js +0 -31
- package/lib/pythonServer/streamlit/generatedPythonFiles.d.ts +0 -2
- package/lib/pythonServer/streamlit/generatedPythonFiles.js +0 -147
- package/lib/pythonServer/tornado/generatedPythonFiles.d.ts +0 -3
- package/lib/pythonServer/tornado/generatedPythonFiles.js +0 -456
- package/src/pythonServer/common/generatedPythonFiles.ts +0 -73
- package/src/pythonServer/dash/generatedPythonFiles.ts +0 -32
- package/src/pythonServer/streamlit/generatedPythonFiles.ts +0 -148
- package/src/pythonServer/tornado/generatedPythonFiles.ts +0 -457
|
@@ -1,15 +1,31 @@
|
|
|
1
1
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
2
2
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
3
3
|
import { PYTHON_SERVER } from '../pythonServer';
|
|
4
|
+
import { Signal } from '@lumino/signaling';
|
|
5
|
+
import { CommBroadcastManager } from './comm';
|
|
6
|
+
import { IS_LITE } from '../tools';
|
|
4
7
|
export class PythonWidgetModel {
|
|
5
8
|
constructor(options) {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
this._commBroadcastManager = new CommBroadcastManager();
|
|
6
11
|
this._isDisposed = false;
|
|
7
12
|
this._kernelStarted = false;
|
|
13
|
+
this._serverReloaded = new Signal(this);
|
|
14
|
+
this._kernelStatusChanged = new Signal(this);
|
|
8
15
|
this._context = options.context;
|
|
9
16
|
this._manager = options.manager;
|
|
10
17
|
this._connectionManager = options.connectionManager;
|
|
11
18
|
this._contentsManager = options.contentsManager;
|
|
12
19
|
this._jpackModel = options.jpackModel;
|
|
20
|
+
this._localPath = PathExt.dirname(this._context.localPath);
|
|
21
|
+
this._autoreload = Boolean((_b = (_a = this._jpackModel) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.autoreload);
|
|
22
|
+
this._contentsManager.fileChanged.connect(this._onFileChanged, this);
|
|
23
|
+
}
|
|
24
|
+
get autoreload() {
|
|
25
|
+
return this._autoreload;
|
|
26
|
+
}
|
|
27
|
+
set autoreload(val) {
|
|
28
|
+
this._autoreload = val;
|
|
13
29
|
}
|
|
14
30
|
get isDisposed() {
|
|
15
31
|
return this._isDisposed;
|
|
@@ -17,22 +33,33 @@ export class PythonWidgetModel {
|
|
|
17
33
|
get connectionManager() {
|
|
18
34
|
return this._connectionManager;
|
|
19
35
|
}
|
|
36
|
+
get serverReloaded() {
|
|
37
|
+
return this._serverReloaded;
|
|
38
|
+
}
|
|
39
|
+
get kernelStatusChanged() {
|
|
40
|
+
return this._kernelStatusChanged;
|
|
41
|
+
}
|
|
42
|
+
async reload() {
|
|
43
|
+
var _a;
|
|
44
|
+
if (!this._kernelStarted) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { spkContent, entryContent } = await this._loadData();
|
|
48
|
+
await ((_a = this._executor) === null || _a === void 0 ? void 0 : _a.reloadPythonServer({
|
|
49
|
+
entryPath: spkContent.entry,
|
|
50
|
+
initCode: entryContent.content
|
|
51
|
+
}));
|
|
52
|
+
this._serverReloaded.emit();
|
|
53
|
+
}
|
|
20
54
|
async initialize() {
|
|
21
|
-
var _a
|
|
55
|
+
var _a;
|
|
22
56
|
if (this._kernelStarted) {
|
|
23
57
|
return {
|
|
24
58
|
success: false,
|
|
25
59
|
error: 'Server is called twice'
|
|
26
60
|
};
|
|
27
61
|
}
|
|
28
|
-
const filePath = this.
|
|
29
|
-
const spkContent = this._jpackModel;
|
|
30
|
-
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
31
|
-
const rootUrl = (_a = spkContent.rootUrl) !== null && _a !== void 0 ? _a : '/';
|
|
32
|
-
const entryContent = await this._contentsManager.get(entryPath, {
|
|
33
|
-
content: true,
|
|
34
|
-
format: 'text'
|
|
35
|
-
});
|
|
62
|
+
const { filePath, spkContent, rootUrl, entryContent } = await this._loadData();
|
|
36
63
|
const sessionManager = this._manager.sessions;
|
|
37
64
|
await sessionManager.ready;
|
|
38
65
|
await this._manager.kernelspecs.ready;
|
|
@@ -55,7 +82,18 @@ export class PythonWidgetModel {
|
|
|
55
82
|
name: kernelName
|
|
56
83
|
},
|
|
57
84
|
type: 'notebook'
|
|
85
|
+
}, {
|
|
86
|
+
kernelConnectionOptions: { handleComms: true }
|
|
58
87
|
});
|
|
88
|
+
const kernel = this._sessionConnection.kernel;
|
|
89
|
+
if (kernel) {
|
|
90
|
+
this._kernelStatusChanged.emit('started');
|
|
91
|
+
this._commBroadcastManager.registerKernel(kernel);
|
|
92
|
+
kernel.disposed.connect(() => {
|
|
93
|
+
this._kernelStatusChanged.emit('stopped');
|
|
94
|
+
this._commBroadcastManager.unregisterKernel(kernel.id);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
59
97
|
const framework = spkContent.framework;
|
|
60
98
|
const ServerClass = PYTHON_SERVER.get(framework);
|
|
61
99
|
if (!ServerClass) {
|
|
@@ -81,17 +119,43 @@ export class PythonWidgetModel {
|
|
|
81
119
|
finish.resolve();
|
|
82
120
|
}
|
|
83
121
|
};
|
|
84
|
-
(
|
|
122
|
+
(_a = this._sessionConnection.kernel) === null || _a === void 0 ? void 0 : _a.statusChanged.connect(cb);
|
|
85
123
|
await finish.promise;
|
|
86
124
|
this._kernelStarted = true;
|
|
87
125
|
return { ...data, rootUrl, framework, success: true };
|
|
88
126
|
}
|
|
89
|
-
dispose() {
|
|
90
|
-
var _a;
|
|
127
|
+
async dispose() {
|
|
128
|
+
var _a, _b, _c;
|
|
91
129
|
if (this._isDisposed) {
|
|
92
130
|
return;
|
|
93
131
|
}
|
|
94
|
-
|
|
132
|
+
if (!IS_LITE) {
|
|
133
|
+
(_b = (_a = this._sessionConnection) === null || _a === void 0 ? void 0 : _a.kernel) === null || _b === void 0 ? void 0 : _b.shutdown();
|
|
134
|
+
}
|
|
135
|
+
void ((_c = this._executor) === null || _c === void 0 ? void 0 : _c.disposePythonServer());
|
|
136
|
+
this._contentsManager.fileChanged.disconnect(this._onFileChanged);
|
|
137
|
+
this._commBroadcastManager.dispose();
|
|
95
138
|
this._isDisposed = true;
|
|
96
139
|
}
|
|
140
|
+
async _loadData() {
|
|
141
|
+
var _a;
|
|
142
|
+
const filePath = this._context.localPath;
|
|
143
|
+
const spkContent = this._jpackModel;
|
|
144
|
+
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
145
|
+
const rootUrl = (_a = spkContent.rootUrl) !== null && _a !== void 0 ? _a : '/';
|
|
146
|
+
const entryContent = await this._contentsManager.get(entryPath, {
|
|
147
|
+
content: true,
|
|
148
|
+
format: 'text'
|
|
149
|
+
});
|
|
150
|
+
return { filePath, spkContent, rootUrl, entryContent };
|
|
151
|
+
}
|
|
152
|
+
async _onFileChanged(sender, args) {
|
|
153
|
+
var _a;
|
|
154
|
+
if (this._autoreload && args.type === 'save') {
|
|
155
|
+
if (((_a = args.newValue) === null || _a === void 0 ? void 0 : _a.path) &&
|
|
156
|
+
args.newValue.path.startsWith(this._localPath)) {
|
|
157
|
+
await this.reload();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
97
161
|
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { Contents } from '@jupyterlab/services';
|
|
2
2
|
import { ISignal } from '@lumino/signaling';
|
|
3
3
|
import { IDict } from '../type';
|
|
4
|
-
|
|
4
|
+
import { IDisposable } from '@lumino/disposable';
|
|
5
|
+
export declare class SandpackFilesModel implements IDisposable {
|
|
5
6
|
constructor(options: {
|
|
6
7
|
contentsManager: Contents.IManager;
|
|
7
8
|
path: string;
|
|
8
9
|
});
|
|
9
|
-
getAllFiles(): Promise<IDict<{
|
|
10
|
+
getAllFiles(force?: boolean): Promise<IDict<{
|
|
10
11
|
code: string;
|
|
11
12
|
}>>;
|
|
13
|
+
get isDisposed(): boolean;
|
|
12
14
|
get fileChanged(): ISignal<SandpackFilesModel, {
|
|
13
15
|
allFiles: IDict<{
|
|
14
16
|
code: string;
|
|
15
17
|
}>;
|
|
16
18
|
}>;
|
|
19
|
+
dispose(): void;
|
|
17
20
|
flattenDirectory(dirContent: Contents.IModel): Promise<IDict<{
|
|
18
21
|
code: string;
|
|
19
22
|
}>>;
|
|
@@ -24,4 +27,5 @@ export declare class SandpackFilesModel {
|
|
|
24
27
|
private _fileChanged;
|
|
25
28
|
private _contentManager;
|
|
26
29
|
private _allFiles?;
|
|
30
|
+
private _isDisposed;
|
|
27
31
|
}
|
|
@@ -3,12 +3,13 @@ import { removePrefix } from '../tools';
|
|
|
3
3
|
export class SandpackFilesModel {
|
|
4
4
|
constructor(options) {
|
|
5
5
|
this._fileChanged = new Signal(this);
|
|
6
|
+
this._isDisposed = false;
|
|
6
7
|
this._contentManager = options.contentsManager;
|
|
7
8
|
this._path = options.path;
|
|
8
9
|
this._contentManager.fileChanged.connect(this._onFileChanged, this);
|
|
9
10
|
}
|
|
10
|
-
async getAllFiles() {
|
|
11
|
-
if (!this._allFiles) {
|
|
11
|
+
async getAllFiles(force = false) {
|
|
12
|
+
if (!this._allFiles || force) {
|
|
12
13
|
const files = await this._contentManager.get(this._path, {
|
|
13
14
|
content: true
|
|
14
15
|
});
|
|
@@ -16,9 +17,19 @@ export class SandpackFilesModel {
|
|
|
16
17
|
}
|
|
17
18
|
return this._allFiles;
|
|
18
19
|
}
|
|
20
|
+
get isDisposed() {
|
|
21
|
+
return this._isDisposed;
|
|
22
|
+
}
|
|
19
23
|
get fileChanged() {
|
|
20
24
|
return this._fileChanged;
|
|
21
25
|
}
|
|
26
|
+
dispose() {
|
|
27
|
+
if (this._isDisposed) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this._isDisposed = true;
|
|
31
|
+
this._contentManager.fileChanged.disconnect(this._onFileChanged);
|
|
32
|
+
}
|
|
22
33
|
async flattenDirectory(dirContent) {
|
|
23
34
|
const flatDict = {};
|
|
24
35
|
const content = dirContent.content;
|
|
@@ -2,15 +2,24 @@ import { SandpackClient } from '@codesandbox/sandpack-client';
|
|
|
2
2
|
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
3
3
|
import { Contents } from '@jupyterlab/services';
|
|
4
4
|
import { IFramePanel } from '../document/iframePanel';
|
|
5
|
+
import { IJupyterPackFileFormat } from '../type';
|
|
5
6
|
import { SandpackFilesModel } from './sandpackFilesModel';
|
|
6
7
|
export declare class SandpackPanel extends IFramePanel {
|
|
7
8
|
constructor(options: {
|
|
8
9
|
context: DocumentRegistry.IContext<DocumentRegistry.IModel>;
|
|
9
10
|
contentsManager: Contents.IManager;
|
|
10
11
|
});
|
|
11
|
-
|
|
12
|
+
dispose(): void;
|
|
13
|
+
get isReady(): Promise<void>;
|
|
14
|
+
get autoreload(): boolean;
|
|
15
|
+
set autoreload(val: boolean);
|
|
16
|
+
init(localPath: string, jpackModel: IJupyterPackFileFormat): Promise<void>;
|
|
12
17
|
connectSignals(filesModel: SandpackFilesModel, sandpackClient: SandpackClient): Promise<void>;
|
|
18
|
+
reload(): Promise<void>;
|
|
13
19
|
private _onFileChanged;
|
|
14
20
|
private _spClient?;
|
|
15
21
|
private _contentsManager;
|
|
22
|
+
private _fileModel;
|
|
23
|
+
private _autoreload;
|
|
24
|
+
private _isReady;
|
|
16
25
|
}
|
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
import { loadSandpackClient } from '@codesandbox/sandpack-client';
|
|
2
2
|
import { IFramePanel } from '../document/iframePanel';
|
|
3
3
|
import { SandpackFilesModel } from './sandpackFilesModel';
|
|
4
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
4
5
|
export class SandpackPanel extends IFramePanel {
|
|
5
6
|
constructor(options) {
|
|
6
7
|
super();
|
|
8
|
+
this._autoreload = false;
|
|
9
|
+
this._isReady = new PromiseDelegate();
|
|
7
10
|
this._contentsManager = options.contentsManager;
|
|
11
|
+
const { context } = options;
|
|
8
12
|
options.context.ready.then(async () => {
|
|
9
|
-
|
|
13
|
+
const jpackModel = context.model.toJSON();
|
|
14
|
+
await this.init(context.localPath, jpackModel);
|
|
10
15
|
});
|
|
11
16
|
}
|
|
12
|
-
|
|
17
|
+
dispose() {
|
|
18
|
+
var _a, _b, _c;
|
|
19
|
+
(_a = this._fileModel) === null || _a === void 0 ? void 0 : _a.fileChanged.disconnect(this._onFileChanged);
|
|
20
|
+
(_b = this._fileModel) === null || _b === void 0 ? void 0 : _b.dispose();
|
|
21
|
+
(_c = this._spClient) === null || _c === void 0 ? void 0 : _c.destroy();
|
|
22
|
+
super.dispose();
|
|
23
|
+
}
|
|
24
|
+
get isReady() {
|
|
25
|
+
return this._isReady.promise;
|
|
26
|
+
}
|
|
27
|
+
get autoreload() {
|
|
28
|
+
return this._autoreload;
|
|
29
|
+
}
|
|
30
|
+
set autoreload(val) {
|
|
31
|
+
this._autoreload = val;
|
|
32
|
+
}
|
|
33
|
+
async init(localPath, jpackModel) {
|
|
34
|
+
var _a;
|
|
35
|
+
if (((_a = jpackModel === null || jpackModel === void 0 ? void 0 : jpackModel.metadata) === null || _a === void 0 ? void 0 : _a.autoreload) === true) {
|
|
36
|
+
this._autoreload = true;
|
|
37
|
+
}
|
|
13
38
|
const currentDir = localPath.split('/').slice(0, -1).join('/');
|
|
14
39
|
const filesModel = new SandpackFilesModel({
|
|
15
40
|
contentsManager: this._contentsManager,
|
|
16
41
|
path: currentDir
|
|
17
42
|
});
|
|
43
|
+
this._fileModel = filesModel;
|
|
18
44
|
const allFiles = await filesModel.getAllFiles();
|
|
19
45
|
const options = {
|
|
20
46
|
showLoadingScreen: true,
|
|
@@ -24,6 +50,7 @@ export class SandpackPanel extends IFramePanel {
|
|
|
24
50
|
files: allFiles
|
|
25
51
|
}, options);
|
|
26
52
|
await this.connectSignals(filesModel, this._spClient);
|
|
53
|
+
this._isReady.resolve();
|
|
27
54
|
}
|
|
28
55
|
async connectSignals(filesModel, sandpackClient) {
|
|
29
56
|
filesModel.fileChanged.connect(this._onFileChanged, this);
|
|
@@ -42,8 +69,16 @@ export class SandpackPanel extends IFramePanel {
|
|
|
42
69
|
}
|
|
43
70
|
});
|
|
44
71
|
}
|
|
72
|
+
async reload() {
|
|
73
|
+
if (this._spClient && this._fileModel) {
|
|
74
|
+
const allFiles = await this._fileModel.getAllFiles();
|
|
75
|
+
this._spClient.updateSandbox({
|
|
76
|
+
files: allFiles
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
45
80
|
_onFileChanged(sender, { allFiles }) {
|
|
46
|
-
if (this._spClient) {
|
|
81
|
+
if (this._autoreload && this._spClient) {
|
|
47
82
|
this._spClient.updateSandbox({
|
|
48
83
|
files: allFiles
|
|
49
84
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IConnectionManager, IDict
|
|
1
|
+
import { IBasePythonServer, IConnectionManager, IDict } from '../type';
|
|
2
2
|
/**
|
|
3
3
|
* Manages connections between clients and kernel executors.
|
|
4
4
|
* This class handles the registration of kernel executors and the generation of responses
|
|
@@ -10,7 +10,7 @@ import { IConnectionManager, IDict, IKernelExecutor } from '../type';
|
|
|
10
10
|
export declare class ConnectionManager implements IConnectionManager {
|
|
11
11
|
instanceId: string;
|
|
12
12
|
constructor(instanceId: string);
|
|
13
|
-
registerConnection(
|
|
13
|
+
registerConnection(pythonServer: IBasePythonServer): Promise<{
|
|
14
14
|
instanceId: string;
|
|
15
15
|
kernelClientId: string;
|
|
16
16
|
}>;
|
|
@@ -23,6 +23,6 @@ export declare class ConnectionManager implements IConnectionManager {
|
|
|
23
23
|
params?: string;
|
|
24
24
|
}): Promise<IDict | null>;
|
|
25
25
|
private _initWsChannel;
|
|
26
|
-
private
|
|
27
|
-
private
|
|
26
|
+
private _pythonServers;
|
|
27
|
+
private _wsBroadcastChannelMap;
|
|
28
28
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { arrayBufferToBase64 } from '../tools';
|
|
1
|
+
import { arrayBufferToBase64, stringToBase64 } from '../tools';
|
|
2
2
|
import { UUID } from '@lumino/coreutils';
|
|
3
3
|
/**
|
|
4
4
|
* Manages connections between clients and kernel executors.
|
|
@@ -11,18 +11,20 @@ import { UUID } from '@lumino/coreutils';
|
|
|
11
11
|
export class ConnectionManager {
|
|
12
12
|
constructor(instanceId) {
|
|
13
13
|
this.instanceId = instanceId;
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
16
|
-
this._initWsChannel();
|
|
14
|
+
this._pythonServers = new Map();
|
|
15
|
+
this._wsBroadcastChannelMap = new Map();
|
|
17
16
|
}
|
|
18
|
-
async registerConnection(
|
|
17
|
+
async registerConnection(pythonServer) {
|
|
19
18
|
const uuid = UUID.uuid4();
|
|
20
|
-
this.
|
|
19
|
+
this._pythonServers.set(uuid, pythonServer);
|
|
20
|
+
const wsbc = new BroadcastChannel(`/jupyterpack/ws/${this.instanceId}/${uuid}`);
|
|
21
|
+
this._initWsChannel(wsbc);
|
|
22
|
+
this._wsBroadcastChannelMap.set(`${this.instanceId}/${uuid}`, wsbc);
|
|
21
23
|
return { instanceId: this.instanceId, kernelClientId: uuid };
|
|
22
24
|
}
|
|
23
25
|
async generateResponse(options) {
|
|
24
26
|
const { urlPath, kernelClientId, method, params, requestBody, headers } = options;
|
|
25
|
-
const executor = this.
|
|
27
|
+
const executor = this._pythonServers.get(kernelClientId);
|
|
26
28
|
if (!executor) {
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
@@ -35,8 +37,8 @@ export class ConnectionManager {
|
|
|
35
37
|
});
|
|
36
38
|
return response;
|
|
37
39
|
}
|
|
38
|
-
_initWsChannel() {
|
|
39
|
-
|
|
40
|
+
_initWsChannel(broadcastChannel) {
|
|
41
|
+
broadcastChannel.onmessage = event => {
|
|
40
42
|
const rawData = event.data;
|
|
41
43
|
let data;
|
|
42
44
|
if (typeof rawData === 'string') {
|
|
@@ -46,9 +48,9 @@ export class ConnectionManager {
|
|
|
46
48
|
data = rawData;
|
|
47
49
|
}
|
|
48
50
|
const { action, dest, wsUrl, payload } = data;
|
|
49
|
-
const executor = this.
|
|
51
|
+
const executor = this._pythonServers.get(dest);
|
|
50
52
|
if (!executor) {
|
|
51
|
-
console.error('Missing kernel handle for message', data, dest, this.
|
|
53
|
+
console.error('Missing kernel handle for message', data, dest, this._pythonServers);
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
switch (action) {
|
|
@@ -61,6 +63,14 @@ export class ConnectionManager {
|
|
|
61
63
|
});
|
|
62
64
|
break;
|
|
63
65
|
}
|
|
66
|
+
case 'close': {
|
|
67
|
+
executor.closeWebsocket({
|
|
68
|
+
instanceId: this.instanceId,
|
|
69
|
+
kernelId: dest,
|
|
70
|
+
wsUrl
|
|
71
|
+
});
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
64
74
|
case 'send': {
|
|
65
75
|
let serializedData;
|
|
66
76
|
let isBinary;
|
|
@@ -70,7 +80,8 @@ export class ConnectionManager {
|
|
|
70
80
|
isBinary = true;
|
|
71
81
|
}
|
|
72
82
|
else if (typeof payload === 'string') {
|
|
73
|
-
|
|
83
|
+
// convert string to base64 string to avoid encoding problem
|
|
84
|
+
serializedData = stringToBase64(payload);
|
|
74
85
|
isBinary = false;
|
|
75
86
|
}
|
|
76
87
|
else {
|
package/lib/token.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Token } from '@lumino/coreutils';
|
|
2
|
-
import { IConnectionManager } from './type';
|
|
2
|
+
import { IConnectionManager, IJupyterpackDocTracker } from './type';
|
|
3
3
|
export declare const IConnectionManagerToken: Token<IConnectionManager>;
|
|
4
|
+
export declare const IJupyterpackDocTrackerToken: Token<IJupyterpackDocTracker>;
|
package/lib/token.js
CHANGED
package/lib/tools.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
2
|
+
export declare const IS_LITE: boolean;
|
|
3
|
+
export declare const logoIcon: LabIcon;
|
|
4
|
+
export declare const autoReloadIcon: LabIcon;
|
|
5
|
+
export declare const linkIcon: LabIcon;
|
|
1
6
|
export declare function removePrefix(path: string, prefix: string): string;
|
|
2
7
|
export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
|
|
3
8
|
export declare function base64ToArrayBuffer(base64: string): Uint8Array;
|
|
4
9
|
export declare function base64ToString(base64: string): string;
|
|
10
|
+
export declare function stringToBase64(str: string): string;
|
|
5
11
|
export declare function stringOrNone(content?: string): string;
|
|
6
12
|
export declare function isBinaryContentType(contentType?: string): boolean;
|
package/lib/tools.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import logoStr from '../style/icons/box.svg';
|
|
2
|
+
import autoReloadStr from '../style/icons/autoreload.svg';
|
|
3
|
+
import linkStr from '../style/icons/externallink.svg';
|
|
4
|
+
import { LabIcon } from '@jupyterlab/ui-components';
|
|
5
|
+
export const IS_LITE = !!document.getElementById('jupyter-lite-main');
|
|
6
|
+
export const logoIcon = new LabIcon({
|
|
7
|
+
name: 'jupyterpack:logo',
|
|
8
|
+
svgstr: logoStr
|
|
9
|
+
});
|
|
10
|
+
export const autoReloadIcon = new LabIcon({
|
|
11
|
+
name: 'jupyterpack:autoReload',
|
|
12
|
+
svgstr: autoReloadStr
|
|
13
|
+
});
|
|
14
|
+
export const linkIcon = new LabIcon({
|
|
15
|
+
name: 'jupyterpack:externalLink',
|
|
16
|
+
svgstr: linkStr
|
|
17
|
+
});
|
|
1
18
|
export function removePrefix(path, prefix) {
|
|
2
19
|
if (path.startsWith(prefix)) {
|
|
3
20
|
return path.slice(prefix.length);
|
|
@@ -27,6 +44,14 @@ export function base64ToString(base64) {
|
|
|
27
44
|
const bytes = base64ToArrayBuffer(base64);
|
|
28
45
|
return new TextDecoder('utf-8').decode(bytes);
|
|
29
46
|
}
|
|
47
|
+
export function stringToBase64(str) {
|
|
48
|
+
const bytes = new TextEncoder().encode(str);
|
|
49
|
+
let binary = '';
|
|
50
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
51
|
+
binary += String.fromCharCode(bytes[i]);
|
|
52
|
+
}
|
|
53
|
+
return window.btoa(binary);
|
|
54
|
+
}
|
|
30
55
|
export function stringOrNone(content) {
|
|
31
56
|
return content ? `"${content}"` : 'None';
|
|
32
57
|
}
|
package/lib/type.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { DocumentWidget } from '@jupyterlab/docregistry';
|
|
1
2
|
import { KernelMessage } from '@jupyterlab/services';
|
|
2
3
|
import { IDisposable } from '@lumino/disposable';
|
|
4
|
+
import { IWidgetTracker } from '@jupyterlab/apputils';
|
|
5
|
+
import { ISignal } from '@lumino/signaling';
|
|
3
6
|
export interface IDict<T = any> {
|
|
4
7
|
[key: string]: T;
|
|
5
8
|
}
|
|
@@ -13,13 +16,17 @@ export declare enum JupyterPackFramework {
|
|
|
13
16
|
REACT = "react",
|
|
14
17
|
DASH = "dash",
|
|
15
18
|
STREAMLIT = "streamlit",
|
|
16
|
-
TORNADO = "tornado"
|
|
19
|
+
TORNADO = "tornado",
|
|
20
|
+
SHINY = "shiny",
|
|
21
|
+
STARLETTE = "starlette"
|
|
17
22
|
}
|
|
18
23
|
export interface IJupyterPackFileFormat {
|
|
19
24
|
entry: string;
|
|
20
25
|
framework: JupyterPackFramework;
|
|
21
26
|
name?: string;
|
|
22
|
-
metadata?:
|
|
27
|
+
metadata?: {
|
|
28
|
+
autoreload?: boolean;
|
|
29
|
+
};
|
|
23
30
|
rootUrl?: string;
|
|
24
31
|
}
|
|
25
32
|
export declare enum MessageAction {
|
|
@@ -33,6 +40,9 @@ export interface IKernelExecutorParams {
|
|
|
33
40
|
requestBody?: ArrayBuffer;
|
|
34
41
|
}
|
|
35
42
|
export interface IKernelExecutor extends IDisposable {
|
|
43
|
+
executeCode(code: KernelMessage.IExecuteRequestMsg['content'], waitForResult?: boolean): Promise<string | null>;
|
|
44
|
+
}
|
|
45
|
+
export interface IBasePythonServer extends IDisposable {
|
|
36
46
|
getResponse(options: IKernelExecutorParams): Promise<IDict>;
|
|
37
47
|
openWebsocket(options: {
|
|
38
48
|
instanceId: string;
|
|
@@ -40,13 +50,17 @@ export interface IKernelExecutor extends IDisposable {
|
|
|
40
50
|
wsUrl: string;
|
|
41
51
|
protocol?: string;
|
|
42
52
|
}): Promise<void>;
|
|
53
|
+
closeWebsocket(options: {
|
|
54
|
+
instanceId: string;
|
|
55
|
+
kernelId: string;
|
|
56
|
+
wsUrl: string;
|
|
57
|
+
}): Promise<void>;
|
|
43
58
|
sendWebsocketMessage(options: {
|
|
44
59
|
instanceId: string;
|
|
45
60
|
kernelId: string;
|
|
46
61
|
wsUrl: string;
|
|
47
62
|
message: string;
|
|
48
63
|
}): Promise<void>;
|
|
49
|
-
executeCode(code: KernelMessage.IExecuteRequestMsg['content'], waitForResult?: boolean): Promise<string | null>;
|
|
50
64
|
init(options: {
|
|
51
65
|
entryPath?: string;
|
|
52
66
|
initCode?: string;
|
|
@@ -54,6 +68,10 @@ export interface IKernelExecutor extends IDisposable {
|
|
|
54
68
|
kernelClientId: string;
|
|
55
69
|
}): Promise<void>;
|
|
56
70
|
disposePythonServer(): Promise<void>;
|
|
71
|
+
reloadPythonServer(options: {
|
|
72
|
+
entryPath?: string;
|
|
73
|
+
initCode?: string;
|
|
74
|
+
}): Promise<void>;
|
|
57
75
|
getResponseFunctionFactory(options: {
|
|
58
76
|
urlPath: string;
|
|
59
77
|
method: string;
|
|
@@ -63,7 +81,7 @@ export interface IKernelExecutor extends IDisposable {
|
|
|
63
81
|
}): string;
|
|
64
82
|
}
|
|
65
83
|
export interface IConnectionManager {
|
|
66
|
-
registerConnection(kernelExecutor:
|
|
84
|
+
registerConnection(kernelExecutor: IBasePythonServer): Promise<{
|
|
67
85
|
instanceId: string;
|
|
68
86
|
kernelClientId: string;
|
|
69
87
|
}>;
|
|
@@ -71,3 +89,20 @@ export interface IConnectionManager {
|
|
|
71
89
|
kernelClientId: string;
|
|
72
90
|
} & IKernelExecutorParams): Promise<IDict | null>;
|
|
73
91
|
}
|
|
92
|
+
export type IJupyterpackDocTracker = IWidgetTracker<DocumentWidget>;
|
|
93
|
+
export interface IPythonWidgetModel extends IDisposable {
|
|
94
|
+
connectionManager: IConnectionManager;
|
|
95
|
+
serverReloaded: ISignal<IPythonWidgetModel, void>;
|
|
96
|
+
kernelStatusChanged: ISignal<IPythonWidgetModel, 'started' | 'stopped'>;
|
|
97
|
+
reload(): Promise<void>;
|
|
98
|
+
initialize(): Promise<{
|
|
99
|
+
success: true;
|
|
100
|
+
instanceId: string;
|
|
101
|
+
kernelClientId: string;
|
|
102
|
+
rootUrl: string;
|
|
103
|
+
framework: JupyterPackFramework;
|
|
104
|
+
} | {
|
|
105
|
+
success: false;
|
|
106
|
+
error: string;
|
|
107
|
+
}>;
|
|
108
|
+
}
|
package/lib/type.js
CHANGED
|
@@ -4,6 +4,8 @@ export var JupyterPackFramework;
|
|
|
4
4
|
JupyterPackFramework["DASH"] = "dash";
|
|
5
5
|
JupyterPackFramework["STREAMLIT"] = "streamlit";
|
|
6
6
|
JupyterPackFramework["TORNADO"] = "tornado";
|
|
7
|
+
JupyterPackFramework["SHINY"] = "shiny";
|
|
8
|
+
JupyterPackFramework["STARLETTE"] = "starlette";
|
|
7
9
|
})(JupyterPackFramework || (JupyterPackFramework = {}));
|
|
8
10
|
export var MessageAction;
|
|
9
11
|
(function (MessageAction) {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
return data;
|
|
38
38
|
};
|
|
39
|
-
const bcWsChannel = new BroadcastChannel(`/jupyterpack/ws/${instanceId}`);
|
|
39
|
+
const bcWsChannel = new BroadcastChannel(`/jupyterpack/ws/${instanceId}/${kernelClientId}`);
|
|
40
40
|
class BroadcastChannelWebSocket {
|
|
41
41
|
constructor(url, protocols) {
|
|
42
42
|
this.onclose = () => {
|
|
@@ -128,6 +128,10 @@
|
|
|
128
128
|
cb();
|
|
129
129
|
}
|
|
130
130
|
this._eventHandlers['close'] = [];
|
|
131
|
+
sendTypedMessage({
|
|
132
|
+
action: 'close',
|
|
133
|
+
wsUrl: this.url
|
|
134
|
+
});
|
|
131
135
|
bcWsChannel.removeEventListener('message', this._bcMessageHandler);
|
|
132
136
|
this.readyState = this.CLOSED;
|
|
133
137
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterpack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "A JupyterLab extension for serving web app.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -29,13 +29,12 @@
|
|
|
29
29
|
"url": "https://github.com/trungleduc/jupyterpack.git"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build:demo": "
|
|
32
|
+
"build:demo": "node script/build-demo.mjs",
|
|
33
33
|
"update:demo": "node script/update-demo.mjs",
|
|
34
|
-
"combine:python": "node script/combine-python.mjs",
|
|
35
34
|
"build:worker": "webpack --config sw.webpack.config.js --mode development",
|
|
36
35
|
"build:worker:prod": "webpack --config sw.webpack.config.js --mode production",
|
|
37
|
-
"build": "jlpm
|
|
38
|
-
"build:prod": "jlpm clean && jlpm
|
|
36
|
+
"build": "jlpm build:lib && jlpm build:labextension:dev && jlpm build:worker",
|
|
37
|
+
"build:prod": "jlpm clean && jlpm build:lib:prod && jlpm build:labextension && jlpm build:worker:prod",
|
|
39
38
|
"build:labextension": "jupyter labextension build .",
|
|
40
39
|
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
41
40
|
"build:lib": "tsc --sourceMap",
|
|
@@ -80,7 +79,7 @@
|
|
|
80
79
|
"eslint-plugin-prettier": "^5.0.0",
|
|
81
80
|
"npm-run-all2": "^7.0.1",
|
|
82
81
|
"prettier": "^3.0.0",
|
|
83
|
-
"rimraf": "^
|
|
82
|
+
"rimraf": "^6.1.0",
|
|
84
83
|
"source-map-loader": "^1.0.2",
|
|
85
84
|
"style-loader": "^3.3.1",
|
|
86
85
|
"stylelint": "^15.10.1",
|
|
@@ -88,6 +87,7 @@
|
|
|
88
87
|
"stylelint-config-standard": "^34.0.0",
|
|
89
88
|
"stylelint-csstree-validator": "^3.0.0",
|
|
90
89
|
"stylelint-prettier": "^4.0.0",
|
|
90
|
+
"tar": "^7.5.2",
|
|
91
91
|
"ts-loader": "^9.2.6",
|
|
92
92
|
"typescript": "~5.8.0",
|
|
93
93
|
"webpack": "^5.76.3",
|