jupyterpack 0.2.0 → 0.3.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/README.md +2 -0
- package/lib/document/widgetFactory.js +4 -1
- package/lib/pythonServer/common/generatedPythonFiles.d.ts +2 -0
- package/lib/pythonServer/common/generatedPythonFiles.js +72 -0
- package/lib/pythonServer/dash/dashServer.d.ts +24 -0
- package/lib/pythonServer/dash/dashServer.js +39 -0
- package/lib/pythonServer/dash/generatedPythonFiles.d.ts +2 -0
- package/lib/pythonServer/dash/generatedPythonFiles.js +31 -0
- package/lib/pythonServer/index.d.ts +5 -0
- package/lib/pythonServer/index.js +9 -0
- package/lib/pythonServer/kernelExecutor.d.ts +66 -0
- package/lib/pythonServer/kernelExecutor.js +133 -0
- package/lib/pythonServer/streamlit/generatedPythonFiles.d.ts +2 -0
- package/lib/pythonServer/streamlit/generatedPythonFiles.js +147 -0
- package/lib/pythonServer/streamlit/streamlitServer.d.ts +33 -0
- package/lib/pythonServer/streamlit/streamlitServer.js +55 -0
- package/lib/pythonServer/tornado/generatedPythonFiles.d.ts +3 -0
- package/lib/pythonServer/tornado/generatedPythonFiles.js +456 -0
- package/lib/pythonServer/tornado/tornadoServer.d.ts +32 -0
- package/lib/pythonServer/tornado/tornadoServer.js +51 -0
- package/lib/pythonWidget/pythonWidget.d.ts +1 -0
- package/lib/pythonWidget/pythonWidget.js +9 -3
- package/lib/pythonWidget/pythonWidgetModel.d.ts +12 -3
- package/lib/pythonWidget/pythonWidgetModel.js +32 -10
- package/lib/swConnection/index.js +2 -2
- package/lib/{pythonWidget/connectionManager.d.ts → swConnection/mainConnectionManager.d.ts} +10 -0
- package/lib/swConnection/mainConnectionManager.js +93 -0
- package/lib/swConnection/sw.js +5 -2
- package/lib/swConnection/swCommManager.d.ts +11 -0
- package/lib/swConnection/{comm_manager.js → swCommManager.js} +11 -3
- package/lib/tools.d.ts +4 -0
- package/lib/tools.js +58 -0
- package/lib/type.d.ts +37 -2
- package/lib/type.js +2 -0
- package/lib/websocket/websocket.d.ts +0 -0
- package/lib/websocket/websocket.js +152 -0
- package/package.json +8 -5
- package/src/document/widgetFactory.ts +4 -1
- package/src/global.d.ts +4 -0
- package/src/pythonServer/common/generatedPythonFiles.ts +73 -0
- package/src/pythonServer/dash/dashServer.ts +57 -0
- package/src/pythonServer/dash/generatedPythonFiles.ts +32 -0
- package/src/pythonServer/index.ts +18 -0
- package/src/pythonServer/kernelExecutor.ts +229 -0
- package/src/pythonServer/streamlit/generatedPythonFiles.ts +148 -0
- package/src/pythonServer/streamlit/streamlitServer.ts +87 -0
- package/src/pythonServer/tornado/generatedPythonFiles.ts +457 -0
- package/src/pythonServer/tornado/tornadoServer.ts +80 -0
- package/src/pythonWidget/pythonWidget.ts +20 -3
- package/src/pythonWidget/pythonWidgetModel.ts +53 -19
- package/src/swConnection/index.ts +5 -2
- package/src/swConnection/mainConnectionManager.ts +121 -0
- package/src/swConnection/sw.ts +5 -2
- package/src/swConnection/{comm_manager.ts → swCommManager.ts} +15 -5
- package/src/tools.ts +69 -0
- package/src/type.ts +47 -3
- package/src/websocket/websocket.ts +216 -0
- package/lib/pythonWidget/connectionManager.js +0 -27
- package/lib/pythonWidget/kernelExecutor.d.ts +0 -27
- package/lib/pythonWidget/kernelExecutor.js +0 -104
- package/lib/swConnection/comm_manager.d.ts +0 -6
- package/lib/swConnection/connection_manager.d.ts +0 -18
- package/lib/swConnection/connection_manager.js +0 -27
- package/src/pythonWidget/connectionManager.ts +0 -43
- package/src/pythonWidget/kernelExecutor.ts +0 -140
- package/src/swConnection/connection_manager.ts +0 -43
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
2
|
-
import { KernelExecutor } from './kernelExecutor';
|
|
3
1
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
2
|
+
import { PromiseDelegate } from '@lumino/coreutils';
|
|
3
|
+
import { PYTHON_SERVER } from '../pythonServer';
|
|
4
4
|
export class PythonWidgetModel {
|
|
5
5
|
constructor(options) {
|
|
6
6
|
this._isDisposed = false;
|
|
@@ -9,6 +9,7 @@ export class PythonWidgetModel {
|
|
|
9
9
|
this._manager = options.manager;
|
|
10
10
|
this._connectionManager = options.connectionManager;
|
|
11
11
|
this._contentsManager = options.contentsManager;
|
|
12
|
+
this._jpackModel = options.jpackModel;
|
|
12
13
|
}
|
|
13
14
|
get isDisposed() {
|
|
14
15
|
return this._isDisposed;
|
|
@@ -17,13 +18,17 @@ export class PythonWidgetModel {
|
|
|
17
18
|
return this._connectionManager;
|
|
18
19
|
}
|
|
19
20
|
async initialize() {
|
|
20
|
-
var _a;
|
|
21
|
+
var _a, _b;
|
|
21
22
|
if (this._kernelStarted) {
|
|
22
|
-
return
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
error: 'Server is called twice'
|
|
26
|
+
};
|
|
23
27
|
}
|
|
24
28
|
const filePath = this._context.localPath;
|
|
25
|
-
const spkContent = this.
|
|
29
|
+
const spkContent = this._jpackModel;
|
|
26
30
|
const entryPath = PathExt.join(PathExt.dirname(filePath), spkContent.entry);
|
|
31
|
+
const rootUrl = (_a = spkContent.rootUrl) !== null && _a !== void 0 ? _a : '/';
|
|
27
32
|
const entryContent = await this._contentsManager.get(entryPath, {
|
|
28
33
|
content: true,
|
|
29
34
|
format: 'text'
|
|
@@ -33,7 +38,10 @@ export class PythonWidgetModel {
|
|
|
33
38
|
await this._manager.kernelspecs.ready;
|
|
34
39
|
const specs = this._manager.kernelspecs.specs;
|
|
35
40
|
if (!specs) {
|
|
36
|
-
return
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
error: 'Missing kernel spec'
|
|
44
|
+
};
|
|
37
45
|
}
|
|
38
46
|
const { kernelspecs } = specs;
|
|
39
47
|
let kernelName = Object.keys(kernelspecs)[0];
|
|
@@ -48,12 +56,21 @@ export class PythonWidgetModel {
|
|
|
48
56
|
},
|
|
49
57
|
type: 'notebook'
|
|
50
58
|
});
|
|
51
|
-
const
|
|
59
|
+
const framework = spkContent.framework;
|
|
60
|
+
const ServerClass = PYTHON_SERVER.get(framework);
|
|
61
|
+
if (!ServerClass) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: `Framework "${framework}" is not supported. Please check your .spk file.`
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const executor = (this._executor = new ServerClass({
|
|
52
68
|
sessionConnection: this._sessionConnection
|
|
53
|
-
});
|
|
69
|
+
}));
|
|
54
70
|
const data = await this._connectionManager.registerConnection(executor);
|
|
55
71
|
await executor.init({
|
|
56
72
|
initCode: entryContent.content,
|
|
73
|
+
entryPath: spkContent.entry,
|
|
57
74
|
...data
|
|
58
75
|
});
|
|
59
76
|
const finish = new PromiseDelegate();
|
|
@@ -64,12 +81,17 @@ export class PythonWidgetModel {
|
|
|
64
81
|
finish.resolve();
|
|
65
82
|
}
|
|
66
83
|
};
|
|
67
|
-
(
|
|
84
|
+
(_b = this._sessionConnection.kernel) === null || _b === void 0 ? void 0 : _b.statusChanged.connect(cb);
|
|
68
85
|
await finish.promise;
|
|
69
86
|
this._kernelStarted = true;
|
|
70
|
-
return data;
|
|
87
|
+
return { ...data, rootUrl, framework, success: true };
|
|
71
88
|
}
|
|
72
89
|
dispose() {
|
|
90
|
+
var _a;
|
|
91
|
+
if (this._isDisposed) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
void ((_a = this._executor) === null || _a === void 0 ? void 0 : _a.disposePythonServer());
|
|
73
95
|
this._isDisposed = true;
|
|
74
96
|
}
|
|
75
97
|
}
|
|
@@ -3,7 +3,7 @@ import { UUID } from '@lumino/coreutils';
|
|
|
3
3
|
import { expose } from 'comlink';
|
|
4
4
|
import { IConnectionManagerToken } from '../token';
|
|
5
5
|
import { MessageAction } from '../type';
|
|
6
|
-
import { ConnectionManager } from './
|
|
6
|
+
import { ConnectionManager } from './mainConnectionManager';
|
|
7
7
|
const fullLabextensionsUrl = PageConfig.getOption('fullLabextensionsUrl');
|
|
8
8
|
const SCOPE = `${fullLabextensionsUrl}/jupyterpack/static`;
|
|
9
9
|
async function initServiceWorker() {
|
|
@@ -50,12 +50,12 @@ export const swPlugin = {
|
|
|
50
50
|
autoStart: true,
|
|
51
51
|
provides: IConnectionManagerToken,
|
|
52
52
|
activate: async (app) => {
|
|
53
|
-
console.log('Activating jupyterpack service worker');
|
|
54
53
|
const serviceWorker = await initServiceWorker();
|
|
55
54
|
if (!serviceWorker) {
|
|
56
55
|
throw new Error('Failed to register the Service Worker, please make sure to use a browser that supports this feature.');
|
|
57
56
|
}
|
|
58
57
|
const instanceId = UUID.uuid4();
|
|
58
|
+
console.log('Activating jupyterpack service worker with instance id', instanceId);
|
|
59
59
|
const { port1: mainToServiceWorker, port2: serviceWorkerToMain } = new MessageChannel();
|
|
60
60
|
const connectionManager = new ConnectionManager(instanceId);
|
|
61
61
|
expose(connectionManager, mainToServiceWorker);
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { IConnectionManager, IDict, IKernelExecutor } from '../type';
|
|
2
|
+
/**
|
|
3
|
+
* Manages connections between clients and kernel executors.
|
|
4
|
+
* This class handles the registration of kernel executors and the generation of responses
|
|
5
|
+
* for client requests. It maintains a mapping of kernel client IDs to their respective executors.
|
|
6
|
+
* The HTTP requests intercepted by the service worker are forwarded to the appropriate kernel executor.
|
|
7
|
+
* The websocket messages forwarded from the broadcast channel are also forwarded to the appropriate kernel executor.
|
|
8
|
+
* It's running on the main thread
|
|
9
|
+
*/
|
|
2
10
|
export declare class ConnectionManager implements IConnectionManager {
|
|
3
11
|
instanceId: string;
|
|
4
12
|
constructor(instanceId: string);
|
|
@@ -14,5 +22,7 @@ export declare class ConnectionManager implements IConnectionManager {
|
|
|
14
22
|
requestBody?: ArrayBuffer;
|
|
15
23
|
params?: string;
|
|
16
24
|
}): Promise<IDict | null>;
|
|
25
|
+
private _initWsChannel;
|
|
17
26
|
private _kernelExecutors;
|
|
27
|
+
private _wsBroadcastChannel;
|
|
18
28
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { arrayBufferToBase64 } from '../tools';
|
|
2
|
+
import { UUID } from '@lumino/coreutils';
|
|
3
|
+
/**
|
|
4
|
+
* Manages connections between clients and kernel executors.
|
|
5
|
+
* This class handles the registration of kernel executors and the generation of responses
|
|
6
|
+
* for client requests. It maintains a mapping of kernel client IDs to their respective executors.
|
|
7
|
+
* The HTTP requests intercepted by the service worker are forwarded to the appropriate kernel executor.
|
|
8
|
+
* The websocket messages forwarded from the broadcast channel are also forwarded to the appropriate kernel executor.
|
|
9
|
+
* It's running on the main thread
|
|
10
|
+
*/
|
|
11
|
+
export class ConnectionManager {
|
|
12
|
+
constructor(instanceId) {
|
|
13
|
+
this.instanceId = instanceId;
|
|
14
|
+
this._kernelExecutors = new Map();
|
|
15
|
+
this._wsBroadcastChannel = new BroadcastChannel(`/jupyterpack/ws/${instanceId}`);
|
|
16
|
+
this._initWsChannel();
|
|
17
|
+
}
|
|
18
|
+
async registerConnection(kernelExecutor) {
|
|
19
|
+
const uuid = UUID.uuid4();
|
|
20
|
+
this._kernelExecutors.set(uuid, kernelExecutor);
|
|
21
|
+
return { instanceId: this.instanceId, kernelClientId: uuid };
|
|
22
|
+
}
|
|
23
|
+
async generateResponse(options) {
|
|
24
|
+
const { urlPath, kernelClientId, method, params, requestBody, headers } = options;
|
|
25
|
+
const executor = this._kernelExecutors.get(kernelClientId);
|
|
26
|
+
if (!executor) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const response = await executor.getResponse({
|
|
30
|
+
urlPath,
|
|
31
|
+
method,
|
|
32
|
+
params,
|
|
33
|
+
headers,
|
|
34
|
+
requestBody
|
|
35
|
+
});
|
|
36
|
+
return response;
|
|
37
|
+
}
|
|
38
|
+
_initWsChannel() {
|
|
39
|
+
this._wsBroadcastChannel.onmessage = event => {
|
|
40
|
+
const rawData = event.data;
|
|
41
|
+
let data;
|
|
42
|
+
if (typeof rawData === 'string') {
|
|
43
|
+
data = JSON.parse(rawData);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
data = rawData;
|
|
47
|
+
}
|
|
48
|
+
const { action, dest, wsUrl, payload } = data;
|
|
49
|
+
const executor = this._kernelExecutors.get(dest);
|
|
50
|
+
if (!executor) {
|
|
51
|
+
console.error('Missing kernel handle for message', data, dest, this._kernelExecutors);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
switch (action) {
|
|
55
|
+
case 'open': {
|
|
56
|
+
executor.openWebsocket({
|
|
57
|
+
instanceId: this.instanceId,
|
|
58
|
+
kernelId: dest,
|
|
59
|
+
wsUrl,
|
|
60
|
+
protocol: payload.protocol
|
|
61
|
+
});
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'send': {
|
|
65
|
+
let serializedData;
|
|
66
|
+
let isBinary;
|
|
67
|
+
if (payload instanceof ArrayBuffer || ArrayBuffer.isView(payload)) {
|
|
68
|
+
// Convert data to base64 string
|
|
69
|
+
serializedData = arrayBufferToBase64(payload);
|
|
70
|
+
isBinary = true;
|
|
71
|
+
}
|
|
72
|
+
else if (typeof payload === 'string') {
|
|
73
|
+
serializedData = payload;
|
|
74
|
+
isBinary = false;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.error('Unknown message type', payload);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
executor.sendWebsocketMessage({
|
|
81
|
+
instanceId: this.instanceId,
|
|
82
|
+
kernelId: dest,
|
|
83
|
+
wsUrl,
|
|
84
|
+
message: JSON.stringify({ isBinary, data: serializedData })
|
|
85
|
+
});
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
package/lib/swConnection/sw.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// import { expose } from 'comlink';
|
|
2
2
|
import { MessageAction } from '../type';
|
|
3
|
-
import { CommManager } from './
|
|
3
|
+
import { CommManager } from './swCommManager';
|
|
4
4
|
const COMM_MANAGER = new CommManager();
|
|
5
5
|
/**
|
|
6
6
|
* Install event listeners
|
|
@@ -32,7 +32,10 @@ async function onFetch(event) {
|
|
|
32
32
|
if (url.endsWith('__jupyterpack__/ping.html')) {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
if (url.includes('extensions/jupyterpack/static')) {
|
|
36
|
+
return event.respondWith(COMM_MANAGER.generateResponse(event.request));
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
36
39
|
}
|
|
37
40
|
function onMessage(msg) {
|
|
38
41
|
const { type, data } = msg.data;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages communication between different components using Comlink's MessagePort-based communication.
|
|
3
|
+
* This class handles registration of communication channels and processing of requests.
|
|
4
|
+
* It's running on the service worker thread
|
|
5
|
+
*/
|
|
6
|
+
export declare class CommManager {
|
|
7
|
+
constructor();
|
|
8
|
+
registerComm(instanceId: string, port: MessagePort): void;
|
|
9
|
+
generateResponse(request: Request): Promise<Response>;
|
|
10
|
+
private _commIds;
|
|
11
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { wrap, transfer } from 'comlink';
|
|
2
|
+
/**
|
|
3
|
+
* Manages communication between different components using Comlink's MessagePort-based communication.
|
|
4
|
+
* This class handles registration of communication channels and processing of requests.
|
|
5
|
+
* It's running on the service worker thread
|
|
6
|
+
*/
|
|
2
7
|
export class CommManager {
|
|
3
8
|
constructor() {
|
|
4
9
|
this._commIds = new Map();
|
|
@@ -19,9 +24,12 @@ export class CommManager {
|
|
|
19
24
|
}
|
|
20
25
|
const params = url.searchParams.toString();
|
|
21
26
|
const pathAfterExtensionName = urlPath.split('/jupyterpack/static')[1];
|
|
22
|
-
const pathList = pathAfterExtensionName.split('/').filter(Boolean);
|
|
23
|
-
const instanceId = pathList[0];
|
|
24
|
-
const kernelClientId = pathList[2];
|
|
27
|
+
const pathList = pathAfterExtensionName === null || pathAfterExtensionName === void 0 ? void 0 : pathAfterExtensionName.split('/').filter(Boolean);
|
|
28
|
+
const instanceId = pathList === null || pathList === void 0 ? void 0 : pathList[0];
|
|
29
|
+
const kernelClientId = pathList === null || pathList === void 0 ? void 0 : pathList[2];
|
|
30
|
+
if (!instanceId || !kernelClientId) {
|
|
31
|
+
return await fetch(url, { method });
|
|
32
|
+
}
|
|
25
33
|
const comm = this._commIds.get(instanceId);
|
|
26
34
|
if (!comm) {
|
|
27
35
|
throw new Error('Missing comm');
|
package/lib/tools.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export declare function removePrefix(path: string, prefix: string): string;
|
|
2
2
|
export declare function arrayBufferToBase64(buffer: ArrayBuffer): string;
|
|
3
|
+
export declare function base64ToArrayBuffer(base64: string): Uint8Array;
|
|
4
|
+
export declare function base64ToString(base64: string): string;
|
|
5
|
+
export declare function stringOrNone(content?: string): string;
|
|
6
|
+
export declare function isBinaryContentType(contentType?: string): boolean;
|
package/lib/tools.js
CHANGED
|
@@ -15,3 +15,61 @@ export function arrayBufferToBase64(buffer) {
|
|
|
15
15
|
}
|
|
16
16
|
return btoa(binary);
|
|
17
17
|
}
|
|
18
|
+
export function base64ToArrayBuffer(base64) {
|
|
19
|
+
const binaryString = atob(base64);
|
|
20
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
21
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
22
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
23
|
+
}
|
|
24
|
+
return bytes;
|
|
25
|
+
}
|
|
26
|
+
export function base64ToString(base64) {
|
|
27
|
+
const bytes = base64ToArrayBuffer(base64);
|
|
28
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
29
|
+
}
|
|
30
|
+
export function stringOrNone(content) {
|
|
31
|
+
return content ? `"${content}"` : 'None';
|
|
32
|
+
}
|
|
33
|
+
export function isBinaryContentType(contentType) {
|
|
34
|
+
if (!contentType) {
|
|
35
|
+
// no Content-Type → assume binary for safety
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
contentType = contentType.toLowerCase().trim();
|
|
39
|
+
const textTypes = [
|
|
40
|
+
'text/',
|
|
41
|
+
'application/json',
|
|
42
|
+
'application/javascript',
|
|
43
|
+
'application/xml',
|
|
44
|
+
'application/xhtml+xml',
|
|
45
|
+
'application/x-www-form-urlencoded',
|
|
46
|
+
'application/sql',
|
|
47
|
+
'application/graphql',
|
|
48
|
+
'application/yaml'
|
|
49
|
+
];
|
|
50
|
+
const binaryIndicators = [
|
|
51
|
+
'image/',
|
|
52
|
+
'audio/',
|
|
53
|
+
'video/',
|
|
54
|
+
'font/',
|
|
55
|
+
'application/octet-stream',
|
|
56
|
+
'application/pdf',
|
|
57
|
+
'application/zip',
|
|
58
|
+
'application/x-protobuf',
|
|
59
|
+
'application/vnd'
|
|
60
|
+
];
|
|
61
|
+
// Starts with text/ or one of the textual types
|
|
62
|
+
if (textTypes.some(t => contentType.startsWith(t))) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// Starts with binary-indicating prefix
|
|
66
|
+
if (binaryIndicators.some(t => contentType.startsWith(t))) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
// If charset is specified → text
|
|
70
|
+
if (contentType.includes('charset=')) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
// Default: assume binary
|
|
74
|
+
return true;
|
|
75
|
+
}
|
package/lib/type.d.ts
CHANGED
|
@@ -3,15 +3,24 @@ import { IDisposable } from '@lumino/disposable';
|
|
|
3
3
|
export interface IDict<T = any> {
|
|
4
4
|
[key: string]: T;
|
|
5
5
|
}
|
|
6
|
+
export interface IBroadcastMessage {
|
|
7
|
+
action: 'message' | 'open' | 'close' | 'error' | 'send' | 'connected' | 'backend_message';
|
|
8
|
+
dest: string;
|
|
9
|
+
wsUrl: string;
|
|
10
|
+
payload?: any;
|
|
11
|
+
}
|
|
6
12
|
export declare enum JupyterPackFramework {
|
|
7
13
|
REACT = "react",
|
|
8
|
-
DASH = "dash"
|
|
14
|
+
DASH = "dash",
|
|
15
|
+
STREAMLIT = "streamlit",
|
|
16
|
+
TORNADO = "tornado"
|
|
9
17
|
}
|
|
10
18
|
export interface IJupyterPackFileFormat {
|
|
11
19
|
entry: string;
|
|
12
20
|
framework: JupyterPackFramework;
|
|
13
21
|
name?: string;
|
|
14
22
|
metadata?: IDict;
|
|
23
|
+
rootUrl?: string;
|
|
15
24
|
}
|
|
16
25
|
export declare enum MessageAction {
|
|
17
26
|
INIT = "INIT"
|
|
@@ -25,7 +34,33 @@ export interface IKernelExecutorParams {
|
|
|
25
34
|
}
|
|
26
35
|
export interface IKernelExecutor extends IDisposable {
|
|
27
36
|
getResponse(options: IKernelExecutorParams): Promise<IDict>;
|
|
28
|
-
|
|
37
|
+
openWebsocket(options: {
|
|
38
|
+
instanceId: string;
|
|
39
|
+
kernelId: string;
|
|
40
|
+
wsUrl: string;
|
|
41
|
+
protocol?: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
sendWebsocketMessage(options: {
|
|
44
|
+
instanceId: string;
|
|
45
|
+
kernelId: string;
|
|
46
|
+
wsUrl: string;
|
|
47
|
+
message: string;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
executeCode(code: KernelMessage.IExecuteRequestMsg['content'], waitForResult?: boolean): Promise<string | null>;
|
|
50
|
+
init(options: {
|
|
51
|
+
entryPath?: string;
|
|
52
|
+
initCode?: string;
|
|
53
|
+
instanceId: string;
|
|
54
|
+
kernelClientId: string;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
disposePythonServer(): Promise<void>;
|
|
57
|
+
getResponseFunctionFactory(options: {
|
|
58
|
+
urlPath: string;
|
|
59
|
+
method: string;
|
|
60
|
+
headers: IDict;
|
|
61
|
+
params?: string;
|
|
62
|
+
content?: string;
|
|
63
|
+
}): string;
|
|
29
64
|
}
|
|
30
65
|
export interface IConnectionManager {
|
|
31
66
|
registerConnection(kernelExecutor: IKernelExecutor): Promise<{
|
package/lib/type.js
CHANGED
|
@@ -2,6 +2,8 @@ export var JupyterPackFramework;
|
|
|
2
2
|
(function (JupyterPackFramework) {
|
|
3
3
|
JupyterPackFramework["REACT"] = "react";
|
|
4
4
|
JupyterPackFramework["DASH"] = "dash";
|
|
5
|
+
JupyterPackFramework["STREAMLIT"] = "streamlit";
|
|
6
|
+
JupyterPackFramework["TORNADO"] = "tornado";
|
|
5
7
|
})(JupyterPackFramework || (JupyterPackFramework = {}));
|
|
6
8
|
export var MessageAction;
|
|
7
9
|
(function (MessageAction) {
|
|
File without changes
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
const urlPath = new URL(window.location.href).pathname;
|
|
4
|
+
const pathAfterExtensionName = urlPath.split('/jupyterpack/static')[1];
|
|
5
|
+
const pathList = pathAfterExtensionName === null || pathAfterExtensionName === void 0 ? void 0 : pathAfterExtensionName.split('/').filter(Boolean);
|
|
6
|
+
const instanceId = pathList === null || pathList === void 0 ? void 0 : pathList[0];
|
|
7
|
+
const kernelClientId = pathList === null || pathList === void 0 ? void 0 : pathList[2];
|
|
8
|
+
if (!instanceId || !kernelClientId) {
|
|
9
|
+
throw new Error('Missing instance Id or kernelClient Id');
|
|
10
|
+
}
|
|
11
|
+
const sendTypedMessage = (msg) => {
|
|
12
|
+
bcWsChannel.postMessage({ ...msg, dest: kernelClientId });
|
|
13
|
+
};
|
|
14
|
+
function base64ToBinary(base64, dataType) {
|
|
15
|
+
const binary = atob(base64); // decode base64
|
|
16
|
+
const len = binary.length;
|
|
17
|
+
const bytes = new Uint8Array(len);
|
|
18
|
+
for (let i = 0; i < len; i++) {
|
|
19
|
+
bytes[i] = binary.charCodeAt(i);
|
|
20
|
+
}
|
|
21
|
+
if (dataType === 'arraybuffer') {
|
|
22
|
+
return bytes.buffer;
|
|
23
|
+
}
|
|
24
|
+
else if (dataType === 'blob') {
|
|
25
|
+
return new Blob([bytes], { type: 'application/octet-stream' });
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error("Unsupported type: use 'arraybuffer' or 'blob'");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const decodeServerMessage = (payload, binaryType) => {
|
|
32
|
+
const { data, isBinary } = payload;
|
|
33
|
+
if (isBinary) {
|
|
34
|
+
// Decode base64 string to array buffer or blob
|
|
35
|
+
return base64ToBinary(data, binaryType);
|
|
36
|
+
}
|
|
37
|
+
return data;
|
|
38
|
+
};
|
|
39
|
+
const bcWsChannel = new BroadcastChannel(`/jupyterpack/ws/${instanceId}`);
|
|
40
|
+
class BroadcastChannelWebSocket {
|
|
41
|
+
constructor(url, protocols) {
|
|
42
|
+
this.onclose = () => {
|
|
43
|
+
// no-op
|
|
44
|
+
};
|
|
45
|
+
this.onerror = () => {
|
|
46
|
+
// no-op
|
|
47
|
+
};
|
|
48
|
+
this.onmessage = () => {
|
|
49
|
+
// no-op
|
|
50
|
+
};
|
|
51
|
+
this.onopen = () => {
|
|
52
|
+
// no-op
|
|
53
|
+
};
|
|
54
|
+
this.CONNECTING = 0;
|
|
55
|
+
this.OPEN = 1;
|
|
56
|
+
this.CLOSING = 2;
|
|
57
|
+
this.CLOSED = 3;
|
|
58
|
+
this._eventHandlers = { message: [], open: [], close: [], error: [] };
|
|
59
|
+
this._bcMessageHandler = async (event) => {
|
|
60
|
+
const rawData = event.data;
|
|
61
|
+
let data;
|
|
62
|
+
if (typeof rawData === 'string') {
|
|
63
|
+
data = JSON.parse(rawData);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
data = rawData;
|
|
67
|
+
}
|
|
68
|
+
const { action, dest, wsUrl, payload } = data;
|
|
69
|
+
if (dest !== kernelClientId || wsUrl !== this.url) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
switch (action) {
|
|
73
|
+
case 'connected': {
|
|
74
|
+
this.readyState = this.OPEN;
|
|
75
|
+
if (this.onopen) {
|
|
76
|
+
this.onopen(event);
|
|
77
|
+
}
|
|
78
|
+
this._eventHandlers.open.forEach(handler => handler({ data: payload }));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'backend_message': {
|
|
82
|
+
const decoded = decodeServerMessage(payload, this.binaryType);
|
|
83
|
+
if (this.onmessage) {
|
|
84
|
+
this.onmessage({ data: decoded });
|
|
85
|
+
}
|
|
86
|
+
this._eventHandlers.message.forEach(handler => handler({ data: decoded }));
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
default:
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
this._open = () => {
|
|
94
|
+
sendTypedMessage({
|
|
95
|
+
action: 'open',
|
|
96
|
+
payload: {
|
|
97
|
+
protocol: this.protocol
|
|
98
|
+
},
|
|
99
|
+
wsUrl: this.url
|
|
100
|
+
});
|
|
101
|
+
bcWsChannel.addEventListener('message', this._bcMessageHandler);
|
|
102
|
+
};
|
|
103
|
+
const urlObj = new URL(url);
|
|
104
|
+
this.url = urlObj.pathname + urlObj.search + urlObj.hash;
|
|
105
|
+
if (protocols) {
|
|
106
|
+
this.protocol = Array.isArray(protocols)
|
|
107
|
+
? protocols.join(',')
|
|
108
|
+
: protocols;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.protocol = '';
|
|
112
|
+
}
|
|
113
|
+
this.binaryType = 'blob';
|
|
114
|
+
this.bufferedAmount = 0;
|
|
115
|
+
this.extensions = '';
|
|
116
|
+
this.readyState = this.CONNECTING;
|
|
117
|
+
this._open();
|
|
118
|
+
}
|
|
119
|
+
close(code, reason) {
|
|
120
|
+
if (this.readyState === this.CLOSED) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (this.onclose) {
|
|
124
|
+
this.onclose();
|
|
125
|
+
}
|
|
126
|
+
while (this._eventHandlers['close'].length) {
|
|
127
|
+
const cb = this._eventHandlers['close'].pop();
|
|
128
|
+
cb();
|
|
129
|
+
}
|
|
130
|
+
this._eventHandlers['close'] = [];
|
|
131
|
+
bcWsChannel.removeEventListener('message', this._bcMessageHandler);
|
|
132
|
+
this.readyState = this.CLOSED;
|
|
133
|
+
}
|
|
134
|
+
send(data) {
|
|
135
|
+
sendTypedMessage({
|
|
136
|
+
action: 'send',
|
|
137
|
+
payload: data,
|
|
138
|
+
wsUrl: this.url
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
addEventListener(type, listener, options) {
|
|
142
|
+
this._eventHandlers[type].push(listener);
|
|
143
|
+
}
|
|
144
|
+
removeEventListener(type, listener, options) {
|
|
145
|
+
this._eventHandlers[type] = this._eventHandlers[type].filter(handler => handler !== listener);
|
|
146
|
+
}
|
|
147
|
+
dispatchEvent(event) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
window.WebSocket = BroadcastChannelWebSocket;
|
|
152
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterpack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A JupyterLab extension for serving web app.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -31,10 +31,11 @@
|
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build:demo": "cd demo && rm -rf .jupyterlite.doit.db _output && jupyter lite build .",
|
|
33
33
|
"update:demo": "node script/update-demo.mjs",
|
|
34
|
+
"combine:python": "node script/combine-python.mjs",
|
|
34
35
|
"build:worker": "webpack --config sw.webpack.config.js --mode development",
|
|
35
36
|
"build:worker:prod": "webpack --config sw.webpack.config.js --mode production",
|
|
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",
|
|
37
|
+
"build": "jlpm combine:python && jlpm build:lib && jlpm build:labextension:dev && jlpm build:worker",
|
|
38
|
+
"build:prod": "jlpm clean && jlpm combine:python && jlpm build:lib:prod && jlpm build:labextension && jlpm build:worker:prod",
|
|
38
39
|
"build:labextension": "jupyter labextension build .",
|
|
39
40
|
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
40
41
|
"build:lib": "tsc --sourceMap",
|
|
@@ -61,7 +62,8 @@
|
|
|
61
62
|
"dependencies": {
|
|
62
63
|
"@codesandbox/sandpack-client": "^2.19.8",
|
|
63
64
|
"@jupyterlab/application": "^4.0.0",
|
|
64
|
-
"comlink": "^4.4.2"
|
|
65
|
+
"comlink": "^4.4.2",
|
|
66
|
+
"strip-ansi": "^7.1.2"
|
|
65
67
|
},
|
|
66
68
|
"devDependencies": {
|
|
67
69
|
"@jupyterlab/builder": "^4.0.0",
|
|
@@ -102,7 +104,8 @@
|
|
|
102
104
|
},
|
|
103
105
|
"jupyterlab": {
|
|
104
106
|
"extension": true,
|
|
105
|
-
"outputDir": "jupyterpack/labextension"
|
|
107
|
+
"outputDir": "jupyterpack/labextension",
|
|
108
|
+
"webpackConfig": "./extension.webpack.config.js"
|
|
106
109
|
},
|
|
107
110
|
"eslintIgnore": [
|
|
108
111
|
"node_modules",
|
|
@@ -49,8 +49,11 @@ export class JupyterPackWidgetFactory extends ABCWidgetFactory<JupyterPackDocWid
|
|
|
49
49
|
content.addWidget(jpContent);
|
|
50
50
|
break;
|
|
51
51
|
}
|
|
52
|
-
case JupyterPackFramework.DASH:
|
|
52
|
+
case JupyterPackFramework.DASH:
|
|
53
|
+
case JupyterPackFramework.STREAMLIT:
|
|
54
|
+
case JupyterPackFramework.TORNADO: {
|
|
53
55
|
const model = new PythonWidgetModel({
|
|
56
|
+
jpackModel,
|
|
54
57
|
context,
|
|
55
58
|
manager: this.options.manager,
|
|
56
59
|
contentsManager: this._contentsManager,
|
package/src/global.d.ts
ADDED