@basthon/kernel-base 0.62.21 → 0.63.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/commons.d.ts +21 -0
- package/lib/commons.js +100 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/kernel.d.ts +49 -14
- package/lib/kernel.js +113 -115
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/worker/comlink-proxy.d.ts +1 -0
- package/lib/worker/comlink-proxy.js +4 -0
- package/lib/worker/index.d.ts +3 -0
- package/lib/worker/index.js +3 -0
- package/lib/worker/kernel-main.d.ts +73 -0
- package/lib/worker/kernel-main.js +203 -0
- package/lib/worker/kernel-worker.d.ts +120 -0
- package/lib/worker/kernel-worker.js +258 -0
- package/lib/worker/proxy.d.ts +10 -0
- package/lib/worker/proxy.js +30 -0
- package/lib/worker/worker-utils.d.ts +6 -0
- package/lib/worker/worker-utils.js +21 -0
- package/package.json +18 -10
package/lib/commons.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Remote } from "comlink";
|
|
2
|
+
import { ProxyFetch } from "./worker/proxy";
|
|
3
|
+
declare global {
|
|
4
|
+
interface Window {
|
|
5
|
+
_remoteProxy: Remote<ProxyFetch>;
|
|
6
|
+
_assetsURL?: string;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An error thrown by not implemented API functions.
|
|
11
|
+
*/
|
|
12
|
+
declare class NotImplementedError extends Error {
|
|
13
|
+
constructor(funcName: string);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* we mock the fetch function to:
|
|
17
|
+
* - redirect queries to local FS
|
|
18
|
+
* - bypass the worker's opaque origin
|
|
19
|
+
*/
|
|
20
|
+
declare const mockFetch: (legacy: boolean) => void;
|
|
21
|
+
export { NotImplementedError, mockFetch };
|
package/lib/commons.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An error thrown by not implemented API functions.
|
|
3
|
+
*/
|
|
4
|
+
class NotImplementedError extends Error {
|
|
5
|
+
constructor(funcName) {
|
|
6
|
+
super(`Function ${funcName} not implemented!`);
|
|
7
|
+
this.name = "NotImplementedError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/* fetch from local FS */
|
|
11
|
+
const fetchFromLocalFS = async (localfsScheme, url) => {
|
|
12
|
+
const toDataURL = async (content) => {
|
|
13
|
+
return await new Promise((resolve, reject) => {
|
|
14
|
+
const reader = new FileReader();
|
|
15
|
+
reader.onloadend = () => {
|
|
16
|
+
resolve(reader.result);
|
|
17
|
+
};
|
|
18
|
+
reader.onerror = reject;
|
|
19
|
+
reader.readAsDataURL(new Blob([content]));
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const prefix = localfsScheme;
|
|
23
|
+
const path = url.slice(prefix.length);
|
|
24
|
+
let content = null;
|
|
25
|
+
const options = {
|
|
26
|
+
status: 200,
|
|
27
|
+
statusText: "OK",
|
|
28
|
+
headers: new Headers(),
|
|
29
|
+
};
|
|
30
|
+
try {
|
|
31
|
+
//@ts-ignore
|
|
32
|
+
content = await self.basthon.getFile(path);
|
|
33
|
+
const dataURL = await toDataURL(content);
|
|
34
|
+
const mime = dataURL.substring(dataURL.indexOf(":") + 1, dataURL.indexOf(";"));
|
|
35
|
+
content = content.buffer;
|
|
36
|
+
options.headers.append("Content-Type", mime);
|
|
37
|
+
options.headers.append("Content-Length", content.byteLength);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
options.status = 404;
|
|
41
|
+
options.statusText = "Not Found";
|
|
42
|
+
}
|
|
43
|
+
return new Response(content, options);
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Get the origin of an url.
|
|
47
|
+
*/
|
|
48
|
+
const origin = (url) => new URL(url).origin;
|
|
49
|
+
/**
|
|
50
|
+
* Get the parent path of an url.
|
|
51
|
+
*/
|
|
52
|
+
const parent = (url) => {
|
|
53
|
+
const abs = new URL(url).href;
|
|
54
|
+
return abs.substring(0, abs.lastIndexOf("/"));
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* we mock the fetch function to:
|
|
58
|
+
* - redirect queries to local FS
|
|
59
|
+
* - bypass the worker's opaque origin
|
|
60
|
+
*/
|
|
61
|
+
const mockFetch = (legacy) => {
|
|
62
|
+
// already mocked?
|
|
63
|
+
if (self.fetch.name === "mockedFetch")
|
|
64
|
+
return;
|
|
65
|
+
const trueFetch = self.fetch;
|
|
66
|
+
async function mockedFetch(request, init) {
|
|
67
|
+
if (!legacy && (self.importScripts == null || self._assetsURL == null))
|
|
68
|
+
return Response.error();
|
|
69
|
+
// force all params to init object
|
|
70
|
+
if (request instanceof Request)
|
|
71
|
+
init = { ...request, ...init };
|
|
72
|
+
// ensure credentials are not sent
|
|
73
|
+
init = { ...init, ...{ credentials: "omit" } };
|
|
74
|
+
const url = request.url ?? request.toString();
|
|
75
|
+
const localfsScheme = "filesystem:/";
|
|
76
|
+
try {
|
|
77
|
+
// requests towards localfs
|
|
78
|
+
if (url.startsWith(localfsScheme)) {
|
|
79
|
+
return await fetchFromLocalFS(localfsScheme, url);
|
|
80
|
+
}
|
|
81
|
+
else if (!legacy && origin(url) === origin(self._assetsURL)) {
|
|
82
|
+
// requests towards same origin
|
|
83
|
+
// only url under the assets folder are proxyied
|
|
84
|
+
if (parent(url).startsWith(self._assetsURL)) {
|
|
85
|
+
const proxy = self._remoteProxy;
|
|
86
|
+
const { body, options } = await proxy.fetch(url, init);
|
|
87
|
+
return new Response(body, options);
|
|
88
|
+
}
|
|
89
|
+
throw new Error("Ressource is outside the proxy' scope");
|
|
90
|
+
}
|
|
91
|
+
return await trueFetch(url, init);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.error(`security proxy: request to ${url} throw error: ${e.toString()}`);
|
|
95
|
+
return Response.error();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
self.fetch = mockedFetch;
|
|
99
|
+
};
|
|
100
|
+
export { NotImplementedError, mockFetch };
|
package/lib/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./kernel";
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./kernel";
|
package/lib/kernel.d.ts
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
|
+
import { PromiseDelegate } from "promise-delegate";
|
|
2
|
+
import { VERSION } from "./version";
|
|
3
|
+
export { VERSION };
|
|
1
4
|
/**
|
|
2
5
|
* API that any Basthon kernel should fill to be supported
|
|
3
6
|
* in console/notebook.
|
|
4
7
|
*/
|
|
5
8
|
export declare class KernelBase {
|
|
6
9
|
private _rootPath;
|
|
10
|
+
private _isReady;
|
|
7
11
|
private _ready;
|
|
8
|
-
private
|
|
12
|
+
private _stopped;
|
|
9
13
|
protected _execution_count: number;
|
|
10
|
-
protected _pendingInput
|
|
14
|
+
protected _pendingInput?: PromiseDelegate<string>;
|
|
11
15
|
private _listeners;
|
|
16
|
+
protected _options: any;
|
|
17
|
+
protected _files: Map<string, ArrayBuffer>;
|
|
18
|
+
protected _modules: Map<string, ArrayBuffer>;
|
|
12
19
|
constructor(options: any);
|
|
20
|
+
/**
|
|
21
|
+
* Is this kernel safe? (loaded from an isolated web worker)
|
|
22
|
+
*/
|
|
23
|
+
safeKernel(): boolean;
|
|
13
24
|
/**
|
|
14
25
|
* Kernel version number (string).
|
|
15
26
|
*/
|
|
@@ -28,10 +39,6 @@ export declare class KernelBase {
|
|
|
28
39
|
* Script (module) file extensions
|
|
29
40
|
*/
|
|
30
41
|
moduleExts(): string[];
|
|
31
|
-
/**
|
|
32
|
-
* Launch the kernel.
|
|
33
|
-
*/
|
|
34
|
-
launch(): Promise<void>;
|
|
35
42
|
/**
|
|
36
43
|
* Execution count getter.
|
|
37
44
|
*/
|
|
@@ -40,7 +47,6 @@ export declare class KernelBase {
|
|
|
40
47
|
* Async code evaluation that resolves with the result.
|
|
41
48
|
*/
|
|
42
49
|
evalAsync(code: string, outCallback: (_: string) => void, errCallback: (_: string) => void, data?: any): Promise<any>;
|
|
43
|
-
restart(): void;
|
|
44
50
|
putFile(filename: string, content: ArrayBuffer): Promise<void>;
|
|
45
51
|
putModule(filename: string, content: ArrayBuffer): Promise<void>;
|
|
46
52
|
userModules(): string[];
|
|
@@ -52,23 +58,48 @@ export declare class KernelBase {
|
|
|
52
58
|
* Get a user module file content.
|
|
53
59
|
*/
|
|
54
60
|
getUserModuleFile(filename: string): Uint8Array;
|
|
55
|
-
more(source: string): boolean
|
|
56
|
-
complete(code: string): [string[], number] | []
|
|
61
|
+
more(source: string): Promise<boolean>;
|
|
62
|
+
complete(code: string): Promise<[string[], number] | []>;
|
|
57
63
|
banner(): string;
|
|
58
64
|
ps1(): string;
|
|
59
65
|
ps2(): string;
|
|
60
66
|
/**
|
|
61
|
-
*
|
|
67
|
+
* Launch the kernel (used to wrap a promise around init).
|
|
68
|
+
*/
|
|
69
|
+
protected _init(): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Initialize the kernel and start it.
|
|
62
72
|
*/
|
|
63
73
|
init(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Start the kernel (used to wrap a promise around start).
|
|
76
|
+
*/
|
|
77
|
+
protected _start(): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Start the kernel.
|
|
80
|
+
*/
|
|
81
|
+
start(): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Perform async part of the stoping process.
|
|
84
|
+
*/
|
|
85
|
+
protected _stop(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Stop the kernel synchronously (to ensure kernel is stopped after the call).
|
|
88
|
+
*/
|
|
89
|
+
stop(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Restart the kernel (stop and start).
|
|
92
|
+
* Use await this.ready() to ensure kernel is started
|
|
93
|
+
*/
|
|
94
|
+
restart(): void;
|
|
64
95
|
/**
|
|
65
96
|
* Is the kernel ready?
|
|
66
97
|
*/
|
|
67
|
-
get
|
|
98
|
+
get isReady(): boolean;
|
|
68
99
|
/**
|
|
69
|
-
* Promise that
|
|
100
|
+
* Promise that resolves when the kernel is ready (started).
|
|
70
101
|
*/
|
|
71
|
-
|
|
102
|
+
ready(): Promise<void>;
|
|
72
103
|
/**
|
|
73
104
|
* Root for kernel files. This is always the language directory
|
|
74
105
|
* inside the version number directory inside the kernel directory.
|
|
@@ -103,7 +134,11 @@ export declare class KernelBase {
|
|
|
103
134
|
/**
|
|
104
135
|
* Send eval.input event then wait for the user response and return it.
|
|
105
136
|
*/
|
|
106
|
-
inputAsync(prompt: string | null | undefined, password?: boolean, data?: any): Promise<
|
|
137
|
+
inputAsync(prompt: string | null | undefined, password?: boolean, data?: any): Promise<string>;
|
|
138
|
+
/**
|
|
139
|
+
* Close a pending input.
|
|
140
|
+
*/
|
|
141
|
+
resolvePendingInput(): void;
|
|
107
142
|
/**
|
|
108
143
|
* Simple clone via JSON copy.
|
|
109
144
|
*/
|
package/lib/kernel.js
CHANGED
|
@@ -1,83 +1,27 @@
|
|
|
1
1
|
import { PromiseDelegate } from "promise-delegate";
|
|
2
2
|
import { VERSION } from "./version";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const path = url.slice(prefix.length);
|
|
26
|
-
let content = null;
|
|
27
|
-
const options = {
|
|
28
|
-
status: 200,
|
|
29
|
-
statusText: "OK",
|
|
30
|
-
headers: new Headers(),
|
|
31
|
-
};
|
|
32
|
-
try {
|
|
33
|
-
//@ts-ignore
|
|
34
|
-
content = window.Basthon.getFile(path);
|
|
35
|
-
const dataURL = await toDataURL(content);
|
|
36
|
-
const mime = dataURL.substring(dataURL.indexOf(":") + 1, dataURL.indexOf(";"));
|
|
37
|
-
content = content.buffer;
|
|
38
|
-
options.headers.append("Content-Type", mime);
|
|
39
|
-
options.headers.append("Content-Length", content.byteLength);
|
|
40
|
-
}
|
|
41
|
-
catch (e) {
|
|
42
|
-
options.status = 404;
|
|
43
|
-
options.statusText = "Not Found";
|
|
44
|
-
}
|
|
45
|
-
return new Response(content, options);
|
|
46
|
-
}
|
|
47
|
-
//@ts-ignore
|
|
48
|
-
return await window._fetch(request, opts);
|
|
49
|
-
};
|
|
50
|
-
const desc = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, "src");
|
|
51
|
-
Object.defineProperty(HTMLImageElement.prototype, "src", {
|
|
52
|
-
...desc,
|
|
53
|
-
get: function () {
|
|
54
|
-
var _a;
|
|
55
|
-
return (_a = desc === null || desc === void 0 ? void 0 : desc.get) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
56
|
-
},
|
|
57
|
-
set: function (e) {
|
|
58
|
-
var _a;
|
|
59
|
-
const prefix = _localfsScheme;
|
|
60
|
-
if (e.startsWith(prefix)) {
|
|
61
|
-
const path = e.slice(prefix.length);
|
|
62
|
-
//@ts-ignore
|
|
63
|
-
const content = window.Basthon.getFile(path).buffer;
|
|
64
|
-
(async () => { var _a; return (_a = desc === null || desc === void 0 ? void 0 : desc.set) === null || _a === void 0 ? void 0 : _a.call(this, await toDataURL(content)); })();
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
(_a = desc === null || desc === void 0 ? void 0 : desc.set) === null || _a === void 0 ? void 0 : _a.call(this, e);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
})();
|
|
72
|
-
/**
|
|
73
|
-
* An error thrown by not implemented API functions.
|
|
74
|
-
*/
|
|
75
|
-
class NotImplementedError extends Error {
|
|
76
|
-
constructor(funcName) {
|
|
77
|
-
super(`Function ${funcName} not implemented!`);
|
|
78
|
-
this.name = "NotImplementedError";
|
|
79
|
-
}
|
|
80
|
-
}
|
|
3
|
+
import { NotImplementedError } from "./commons";
|
|
4
|
+
// const desc = Object.getOwnPropertyDescriptor(
|
|
5
|
+
// HTMLImageElement.prototype,
|
|
6
|
+
// "src"
|
|
7
|
+
// );
|
|
8
|
+
// Object.defineProperty(HTMLImageElement.prototype, "src", {
|
|
9
|
+
// ...desc,
|
|
10
|
+
// get: function () {
|
|
11
|
+
// return desc?.get?.call(this);
|
|
12
|
+
// },
|
|
13
|
+
// set: function (e) {
|
|
14
|
+
// const prefix = _localfsScheme;
|
|
15
|
+
// if (e.startsWith(prefix)) {
|
|
16
|
+
// const path = e.slice(prefix.length);
|
|
17
|
+
// //@ts-ignore
|
|
18
|
+
// const content = window.Basthon.getFile(path).buffer;
|
|
19
|
+
// (async () => desc?.set?.call(this, await toDataURL(content)))();
|
|
20
|
+
// } else {
|
|
21
|
+
// desc?.set?.call(this, e);
|
|
22
|
+
// }
|
|
23
|
+
// },
|
|
24
|
+
// });
|
|
81
25
|
/**
|
|
82
26
|
* Event for Basthon's dispatch/listen.
|
|
83
27
|
*/
|
|
@@ -87,21 +31,32 @@ class BasthonEvent extends Event {
|
|
|
87
31
|
this.detail = data;
|
|
88
32
|
}
|
|
89
33
|
}
|
|
34
|
+
export { VERSION };
|
|
90
35
|
/**
|
|
91
36
|
* API that any Basthon kernel should fill to be supported
|
|
92
37
|
* in console/notebook.
|
|
93
38
|
*/
|
|
94
39
|
export class KernelBase {
|
|
95
40
|
constructor(options) {
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
41
|
+
this._isReady = false;
|
|
42
|
+
this._ready = new PromiseDelegate();
|
|
43
|
+
this._stopped = Promise.resolve();
|
|
98
44
|
this._execution_count = 0;
|
|
99
|
-
this._pendingInput =
|
|
45
|
+
this._pendingInput = undefined;
|
|
100
46
|
// a map to register wrapped listeners to allow later remove
|
|
101
47
|
this._listeners = new Map();
|
|
48
|
+
this._files = new Map();
|
|
49
|
+
this._modules = new Map();
|
|
50
|
+
this._options = this.clone(options);
|
|
102
51
|
// root path where kernel is installed
|
|
103
52
|
this._rootPath = options.rootPath;
|
|
104
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Is this kernel safe? (loaded from an isolated web worker)
|
|
56
|
+
*/
|
|
57
|
+
safeKernel() {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
105
60
|
/**
|
|
106
61
|
* Kernel version number (string).
|
|
107
62
|
*/
|
|
@@ -128,12 +83,6 @@ export class KernelBase {
|
|
|
128
83
|
moduleExts() {
|
|
129
84
|
throw new NotImplementedError("moduleExts");
|
|
130
85
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Launch the kernel.
|
|
133
|
-
*/
|
|
134
|
-
async launch() {
|
|
135
|
-
throw new NotImplementedError("launch");
|
|
136
|
-
}
|
|
137
86
|
/**
|
|
138
87
|
* Execution count getter.
|
|
139
88
|
*/
|
|
@@ -146,14 +95,11 @@ export class KernelBase {
|
|
|
146
95
|
evalAsync(code, outCallback, errCallback, data = null) {
|
|
147
96
|
throw new NotImplementedError("evalAsync");
|
|
148
97
|
}
|
|
149
|
-
restart() {
|
|
150
|
-
throw new NotImplementedError("restart");
|
|
151
|
-
}
|
|
152
98
|
async putFile(filename, content) {
|
|
153
|
-
|
|
99
|
+
this._files.set(filename, content);
|
|
154
100
|
}
|
|
155
101
|
async putModule(filename, content) {
|
|
156
|
-
|
|
102
|
+
this._modules.set(filename, content);
|
|
157
103
|
}
|
|
158
104
|
userModules() {
|
|
159
105
|
return [];
|
|
@@ -170,10 +116,10 @@ export class KernelBase {
|
|
|
170
116
|
getUserModuleFile(filename) {
|
|
171
117
|
throw new NotImplementedError("getUserModuleFile");
|
|
172
118
|
}
|
|
173
|
-
more(source) {
|
|
119
|
+
async more(source) {
|
|
174
120
|
throw new NotImplementedError("more");
|
|
175
121
|
}
|
|
176
|
-
complete(code) {
|
|
122
|
+
async complete(code) {
|
|
177
123
|
return [];
|
|
178
124
|
}
|
|
179
125
|
banner() {
|
|
@@ -186,32 +132,80 @@ export class KernelBase {
|
|
|
186
132
|
return "... ";
|
|
187
133
|
}
|
|
188
134
|
/**
|
|
189
|
-
*
|
|
135
|
+
* Launch the kernel (used to wrap a promise around init).
|
|
136
|
+
*/
|
|
137
|
+
async _init() { }
|
|
138
|
+
/**
|
|
139
|
+
* Initialize the kernel and start it.
|
|
190
140
|
*/
|
|
191
141
|
async init() {
|
|
142
|
+
if (!this.safeKernel())
|
|
143
|
+
console.warn(`%c⚠ DANGER ⚠: this kernel is not safe!` +
|
|
144
|
+
" Please consider using a safe kernel.", "color: red; font-size: 20px;");
|
|
192
145
|
try {
|
|
193
|
-
await this.
|
|
146
|
+
await this._init();
|
|
194
147
|
}
|
|
195
148
|
catch (error) {
|
|
196
|
-
this.
|
|
149
|
+
this._ready.reject(error);
|
|
197
150
|
return;
|
|
198
151
|
}
|
|
199
152
|
// connecting eval to basthon.eval.request event.
|
|
200
153
|
this.addEventListener("eval.request", this.evalFromEvent.bind(this));
|
|
201
|
-
this.
|
|
202
|
-
|
|
154
|
+
await this.start();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Start the kernel (used to wrap a promise around start).
|
|
158
|
+
*/
|
|
159
|
+
async _start() { }
|
|
160
|
+
/**
|
|
161
|
+
* Start the kernel.
|
|
162
|
+
*/
|
|
163
|
+
async start() {
|
|
164
|
+
await this._stopped;
|
|
165
|
+
this._execution_count = 0;
|
|
166
|
+
try {
|
|
167
|
+
await this._start();
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
this._ready.reject(error);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this._files.forEach((v, k) => this.putFile(k, v));
|
|
174
|
+
this._modules.forEach((v, k) => this.putModule(k, v));
|
|
175
|
+
this._isReady = true;
|
|
176
|
+
this._ready.resolve();
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Perform async part of the stoping process.
|
|
180
|
+
*/
|
|
181
|
+
async _stop() { }
|
|
182
|
+
/**
|
|
183
|
+
* Stop the kernel synchronously (to ensure kernel is stopped after the call).
|
|
184
|
+
*/
|
|
185
|
+
stop() {
|
|
186
|
+
this._isReady = false;
|
|
187
|
+
this._ready = new PromiseDelegate();
|
|
188
|
+
this._stopped = this._stop();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Restart the kernel (stop and start).
|
|
192
|
+
* Use await this.ready() to ensure kernel is started
|
|
193
|
+
*/
|
|
194
|
+
restart() {
|
|
195
|
+
this.stop(); // sync
|
|
196
|
+
this.start(); // async
|
|
203
197
|
}
|
|
204
198
|
/**
|
|
205
199
|
* Is the kernel ready?
|
|
206
200
|
*/
|
|
207
|
-
get
|
|
208
|
-
return this.
|
|
201
|
+
get isReady() {
|
|
202
|
+
return this._isReady;
|
|
209
203
|
}
|
|
210
204
|
/**
|
|
211
|
-
* Promise that
|
|
205
|
+
* Promise that resolves when the kernel is ready (started).
|
|
212
206
|
*/
|
|
213
|
-
async
|
|
214
|
-
|
|
207
|
+
async ready() {
|
|
208
|
+
return this._ready.promise;
|
|
215
209
|
}
|
|
216
210
|
/**
|
|
217
211
|
* Root for kernel files. This is always the language directory
|
|
@@ -287,7 +281,7 @@ export class KernelBase {
|
|
|
287
281
|
removeEventListener(eventName, callback) {
|
|
288
282
|
// get the wrapped callback
|
|
289
283
|
const listeners = this._listeners.get(eventName);
|
|
290
|
-
document.removeEventListener(`basthon.${eventName}`, listeners
|
|
284
|
+
document.removeEventListener(`basthon.${eventName}`, listeners?.get(callback));
|
|
291
285
|
}
|
|
292
286
|
/**
|
|
293
287
|
* Send eval.input event then wait for the user response and return it.
|
|
@@ -295,16 +289,21 @@ export class KernelBase {
|
|
|
295
289
|
async inputAsync(prompt, password = false, data = undefined) {
|
|
296
290
|
data = this.clone(data);
|
|
297
291
|
data.content = { prompt, password };
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
this._pendingInput = true;
|
|
292
|
+
const pd = new PromiseDelegate();
|
|
293
|
+
data.resolve = pd.resolve.bind(pd);
|
|
294
|
+
data.reject = pd.reject.bind(pd);
|
|
295
|
+
this._pendingInput = pd;
|
|
303
296
|
this.dispatchEvent("eval.input", data);
|
|
304
|
-
const res = await promise;
|
|
305
|
-
this._pendingInput =
|
|
297
|
+
const res = await pd.promise;
|
|
298
|
+
this._pendingInput = undefined;
|
|
306
299
|
return res;
|
|
307
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Close a pending input.
|
|
303
|
+
*/
|
|
304
|
+
resolvePendingInput() {
|
|
305
|
+
this._pendingInput?.resolve("");
|
|
306
|
+
}
|
|
308
307
|
/**
|
|
309
308
|
* Simple clone via JSON copy.
|
|
310
309
|
*/
|
|
@@ -317,8 +316,7 @@ export class KernelBase {
|
|
|
317
316
|
* Detection is based on extension.
|
|
318
317
|
*/
|
|
319
318
|
async putRessource(filename, content) {
|
|
320
|
-
|
|
321
|
-
const ext = (_a = filename.split(".").pop()) !== null && _a !== void 0 ? _a : "";
|
|
319
|
+
const ext = filename.split(".").pop() ?? "";
|
|
322
320
|
if (this.moduleExts().includes(ext)) {
|
|
323
321
|
return await this.putModule(filename, content);
|
|
324
322
|
}
|
|
@@ -330,7 +328,7 @@ export class KernelBase {
|
|
|
330
328
|
* Is an input pending?
|
|
331
329
|
*/
|
|
332
330
|
pendingInput() {
|
|
333
|
-
return this._pendingInput;
|
|
331
|
+
return this._pendingInput != null;
|
|
334
332
|
}
|
|
335
333
|
/**
|
|
336
334
|
* Internal. Code evaluation after an eval.request event.
|
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.63.0";
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.63.0";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Remote } from "comlink";
|
|
2
|
+
import { KernelWorkerBase } from "./kernel-worker";
|
|
3
|
+
import { KernelBase } from "../kernel";
|
|
4
|
+
export declare class KernelMainBase<Type extends KernelWorkerBase> extends KernelBase {
|
|
5
|
+
private _worker?;
|
|
6
|
+
private _remote?;
|
|
7
|
+
private _legacy;
|
|
8
|
+
constructor(options: any);
|
|
9
|
+
/**
|
|
10
|
+
* Is this a legacy worker? i.e. not runing in a worker.
|
|
11
|
+
*/
|
|
12
|
+
isLegacy(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Is this kernel safe? (loaded in an isolated web worker)
|
|
15
|
+
*/
|
|
16
|
+
safeKernel(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get a new worker (should be overloaded).
|
|
19
|
+
*/
|
|
20
|
+
protected newWorker(): Worker;
|
|
21
|
+
/**
|
|
22
|
+
* Import the code for a legacy worker (running in the main thread).
|
|
23
|
+
*/
|
|
24
|
+
protected importLegacyWorker(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Create the kernel webworker and setup communication.
|
|
27
|
+
*/
|
|
28
|
+
protected setupWorker(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Start the worker.
|
|
31
|
+
*/
|
|
32
|
+
_start(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Comlink remote getter.
|
|
35
|
+
*/
|
|
36
|
+
protected get remote(): Remote<Type> | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Stop the worker.
|
|
39
|
+
*/
|
|
40
|
+
_stop(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Process messages from worker (if any).
|
|
43
|
+
*/
|
|
44
|
+
protected processWorkerMessage(msg: {
|
|
45
|
+
data: any;
|
|
46
|
+
type: string;
|
|
47
|
+
content: any;
|
|
48
|
+
}): void;
|
|
49
|
+
evalAsync(code: string, outCallback: (_: string) => void, errCallback: (_: string) => void, data?: any): Promise<any>;
|
|
50
|
+
/**
|
|
51
|
+
* Put a file on the local (emulated) filesystem.
|
|
52
|
+
*/
|
|
53
|
+
putFile(filename: string, content: ArrayBuffer): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Put an importable module on the local (emulated) filesystem
|
|
56
|
+
* and load dependencies.
|
|
57
|
+
*/
|
|
58
|
+
putModule(filename: string, content: ArrayBuffer): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Complete a code at the end (usefull for tab completion).
|
|
61
|
+
*
|
|
62
|
+
* Returns an array of two elements: the list of completions
|
|
63
|
+
* and the start index.
|
|
64
|
+
*/
|
|
65
|
+
complete(code: string): Promise<[string[], number] | []>;
|
|
66
|
+
/**
|
|
67
|
+
* Tell wether we should wait for more code or if it can
|
|
68
|
+
* be run as is.
|
|
69
|
+
*
|
|
70
|
+
* Useful to set ps1/ps2 in console prompt.
|
|
71
|
+
*/
|
|
72
|
+
more(code: string): Promise<boolean>;
|
|
73
|
+
}
|