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.
Files changed (66) hide show
  1. package/README.md +2 -0
  2. package/lib/document/widgetFactory.js +4 -1
  3. package/lib/pythonServer/common/generatedPythonFiles.d.ts +2 -0
  4. package/lib/pythonServer/common/generatedPythonFiles.js +72 -0
  5. package/lib/pythonServer/dash/dashServer.d.ts +24 -0
  6. package/lib/pythonServer/dash/dashServer.js +39 -0
  7. package/lib/pythonServer/dash/generatedPythonFiles.d.ts +2 -0
  8. package/lib/pythonServer/dash/generatedPythonFiles.js +31 -0
  9. package/lib/pythonServer/index.d.ts +5 -0
  10. package/lib/pythonServer/index.js +9 -0
  11. package/lib/pythonServer/kernelExecutor.d.ts +66 -0
  12. package/lib/pythonServer/kernelExecutor.js +133 -0
  13. package/lib/pythonServer/streamlit/generatedPythonFiles.d.ts +2 -0
  14. package/lib/pythonServer/streamlit/generatedPythonFiles.js +147 -0
  15. package/lib/pythonServer/streamlit/streamlitServer.d.ts +33 -0
  16. package/lib/pythonServer/streamlit/streamlitServer.js +55 -0
  17. package/lib/pythonServer/tornado/generatedPythonFiles.d.ts +3 -0
  18. package/lib/pythonServer/tornado/generatedPythonFiles.js +456 -0
  19. package/lib/pythonServer/tornado/tornadoServer.d.ts +32 -0
  20. package/lib/pythonServer/tornado/tornadoServer.js +51 -0
  21. package/lib/pythonWidget/pythonWidget.d.ts +1 -0
  22. package/lib/pythonWidget/pythonWidget.js +9 -3
  23. package/lib/pythonWidget/pythonWidgetModel.d.ts +12 -3
  24. package/lib/pythonWidget/pythonWidgetModel.js +32 -10
  25. package/lib/swConnection/index.js +2 -2
  26. package/lib/{pythonWidget/connectionManager.d.ts → swConnection/mainConnectionManager.d.ts} +10 -0
  27. package/lib/swConnection/mainConnectionManager.js +93 -0
  28. package/lib/swConnection/sw.js +5 -2
  29. package/lib/swConnection/swCommManager.d.ts +11 -0
  30. package/lib/swConnection/{comm_manager.js → swCommManager.js} +11 -3
  31. package/lib/tools.d.ts +4 -0
  32. package/lib/tools.js +58 -0
  33. package/lib/type.d.ts +37 -2
  34. package/lib/type.js +2 -0
  35. package/lib/websocket/websocket.d.ts +0 -0
  36. package/lib/websocket/websocket.js +152 -0
  37. package/package.json +8 -5
  38. package/src/document/widgetFactory.ts +4 -1
  39. package/src/global.d.ts +4 -0
  40. package/src/pythonServer/common/generatedPythonFiles.ts +73 -0
  41. package/src/pythonServer/dash/dashServer.ts +57 -0
  42. package/src/pythonServer/dash/generatedPythonFiles.ts +32 -0
  43. package/src/pythonServer/index.ts +18 -0
  44. package/src/pythonServer/kernelExecutor.ts +229 -0
  45. package/src/pythonServer/streamlit/generatedPythonFiles.ts +148 -0
  46. package/src/pythonServer/streamlit/streamlitServer.ts +87 -0
  47. package/src/pythonServer/tornado/generatedPythonFiles.ts +457 -0
  48. package/src/pythonServer/tornado/tornadoServer.ts +80 -0
  49. package/src/pythonWidget/pythonWidget.ts +20 -3
  50. package/src/pythonWidget/pythonWidgetModel.ts +53 -19
  51. package/src/swConnection/index.ts +5 -2
  52. package/src/swConnection/mainConnectionManager.ts +121 -0
  53. package/src/swConnection/sw.ts +5 -2
  54. package/src/swConnection/{comm_manager.ts → swCommManager.ts} +15 -5
  55. package/src/tools.ts +69 -0
  56. package/src/type.ts +47 -3
  57. package/src/websocket/websocket.ts +216 -0
  58. package/lib/pythonWidget/connectionManager.js +0 -27
  59. package/lib/pythonWidget/kernelExecutor.d.ts +0 -27
  60. package/lib/pythonWidget/kernelExecutor.js +0 -104
  61. package/lib/swConnection/comm_manager.d.ts +0 -6
  62. package/lib/swConnection/connection_manager.d.ts +0 -18
  63. package/lib/swConnection/connection_manager.js +0 -27
  64. package/src/pythonWidget/connectionManager.ts +0 -43
  65. package/src/pythonWidget/kernelExecutor.ts +0 -140
  66. 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 null;
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._context.model.toJSON();
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 null;
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 executor = new KernelExecutor({
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
- (_a = this._sessionConnection.kernel) === null || _a === void 0 ? void 0 : _a.statusChanged.connect(cb);
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 './connection_manager';
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
+ }
@@ -1,6 +1,6 @@
1
1
  // import { expose } from 'comlink';
2
2
  import { MessageAction } from '../type';
3
- import { CommManager } from './comm_manager';
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
- event.respondWith(COMM_MANAGER.generateResponse(event.request));
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
- executeCode(code: KernelMessage.IExecuteRequestMsg['content']): Promise<string>;
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.2.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,
@@ -0,0 +1,4 @@
1
+ declare module '*?raw' {
2
+ const content: string;
3
+ export default content;
4
+ }