@basthon/kernel-base 0.62.22 → 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 +1 -1
- package/lib/commons.js +4 -4
- package/lib/kernel.js +3 -3
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/worker/index.d.ts +1 -0
- package/lib/worker/index.js +1 -0
- package/lib/worker/kernel-main.d.ts +13 -0
- package/lib/worker/kernel-main.js +50 -10
- package/lib/worker/kernel-worker.d.ts +11 -0
- package/lib/worker/kernel-worker.js +22 -8
- package/lib/worker/worker-utils.d.ts +6 -0
- package/lib/worker/worker-utils.js +21 -0
- package/package.json +2 -2
package/lib/commons.d.ts
CHANGED
|
@@ -17,5 +17,5 @@ declare class NotImplementedError extends Error {
|
|
|
17
17
|
* - redirect queries to local FS
|
|
18
18
|
* - bypass the worker's opaque origin
|
|
19
19
|
*/
|
|
20
|
-
declare const mockFetch: () => void;
|
|
20
|
+
declare const mockFetch: (legacy: boolean) => void;
|
|
21
21
|
export { NotImplementedError, mockFetch };
|
package/lib/commons.js
CHANGED
|
@@ -29,7 +29,7 @@ const fetchFromLocalFS = async (localfsScheme, url) => {
|
|
|
29
29
|
};
|
|
30
30
|
try {
|
|
31
31
|
//@ts-ignore
|
|
32
|
-
content = self.
|
|
32
|
+
content = await self.basthon.getFile(path);
|
|
33
33
|
const dataURL = await toDataURL(content);
|
|
34
34
|
const mime = dataURL.substring(dataURL.indexOf(":") + 1, dataURL.indexOf(";"));
|
|
35
35
|
content = content.buffer;
|
|
@@ -58,13 +58,13 @@ const parent = (url) => {
|
|
|
58
58
|
* - redirect queries to local FS
|
|
59
59
|
* - bypass the worker's opaque origin
|
|
60
60
|
*/
|
|
61
|
-
const mockFetch = () => {
|
|
61
|
+
const mockFetch = (legacy) => {
|
|
62
62
|
// already mocked?
|
|
63
63
|
if (self.fetch.name === "mockedFetch")
|
|
64
64
|
return;
|
|
65
65
|
const trueFetch = self.fetch;
|
|
66
66
|
async function mockedFetch(request, init) {
|
|
67
|
-
if (self.importScripts == null || self._assetsURL == null)
|
|
67
|
+
if (!legacy && (self.importScripts == null || self._assetsURL == null))
|
|
68
68
|
return Response.error();
|
|
69
69
|
// force all params to init object
|
|
70
70
|
if (request instanceof Request)
|
|
@@ -78,7 +78,7 @@ const mockFetch = () => {
|
|
|
78
78
|
if (url.startsWith(localfsScheme)) {
|
|
79
79
|
return await fetchFromLocalFS(localfsScheme, url);
|
|
80
80
|
}
|
|
81
|
-
else if (origin(url) === origin(self._assetsURL)) {
|
|
81
|
+
else if (!legacy && origin(url) === origin(self._assetsURL)) {
|
|
82
82
|
// requests towards same origin
|
|
83
83
|
// only url under the assets folder are proxyied
|
|
84
84
|
if (parent(url).startsWith(self._assetsURL)) {
|
package/lib/kernel.js
CHANGED
|
@@ -50,9 +50,6 @@ export class KernelBase {
|
|
|
50
50
|
this._options = this.clone(options);
|
|
51
51
|
// root path where kernel is installed
|
|
52
52
|
this._rootPath = options.rootPath;
|
|
53
|
-
if (!this.safeKernel())
|
|
54
|
-
console.warn(`%c⚠ DANGER ⚠: kernel ${this.language()} not safe!` +
|
|
55
|
-
" Please consider using a safe kernel.", "color: red; font-size: 20px;");
|
|
56
53
|
}
|
|
57
54
|
/**
|
|
58
55
|
* Is this kernel safe? (loaded from an isolated web worker)
|
|
@@ -142,6 +139,9 @@ export class KernelBase {
|
|
|
142
139
|
* Initialize the kernel and start it.
|
|
143
140
|
*/
|
|
144
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;");
|
|
145
145
|
try {
|
|
146
146
|
await this._init();
|
|
147
147
|
}
|
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";
|
package/lib/worker/index.d.ts
CHANGED
package/lib/worker/index.js
CHANGED
|
@@ -4,7 +4,12 @@ import { KernelBase } from "../kernel";
|
|
|
4
4
|
export declare class KernelMainBase<Type extends KernelWorkerBase> extends KernelBase {
|
|
5
5
|
private _worker?;
|
|
6
6
|
private _remote?;
|
|
7
|
+
private _legacy;
|
|
7
8
|
constructor(options: any);
|
|
9
|
+
/**
|
|
10
|
+
* Is this a legacy worker? i.e. not runing in a worker.
|
|
11
|
+
*/
|
|
12
|
+
isLegacy(): boolean;
|
|
8
13
|
/**
|
|
9
14
|
* Is this kernel safe? (loaded in an isolated web worker)
|
|
10
15
|
*/
|
|
@@ -13,6 +18,14 @@ export declare class KernelMainBase<Type extends KernelWorkerBase> extends Kerne
|
|
|
13
18
|
* Get a new worker (should be overloaded).
|
|
14
19
|
*/
|
|
15
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>;
|
|
16
29
|
/**
|
|
17
30
|
* Start the worker.
|
|
18
31
|
*/
|
|
@@ -5,7 +5,7 @@ import { KernelBase } from "../kernel";
|
|
|
5
5
|
if (globalThis.Worker.name !== "SandboxedWorker") {
|
|
6
6
|
class SandboxedWorker extends Worker {
|
|
7
7
|
constructor(url, options) {
|
|
8
|
-
if (options
|
|
8
|
+
if (options?.sandboxed === true) {
|
|
9
9
|
const absURL = new URL(url).href;
|
|
10
10
|
const assetsURL = absURL.substring(0, absURL.lastIndexOf("/"));
|
|
11
11
|
// worker created with a 'data' url scheme has opaque origin
|
|
@@ -26,12 +26,20 @@ if (globalThis.Worker.name !== "SandboxedWorker") {
|
|
|
26
26
|
export class KernelMainBase extends KernelBase {
|
|
27
27
|
constructor(options) {
|
|
28
28
|
super(options);
|
|
29
|
+
this._legacy = false;
|
|
30
|
+
this._legacy = options?.legacy === true;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Is this a legacy worker? i.e. not runing in a worker.
|
|
34
|
+
*/
|
|
35
|
+
isLegacy() {
|
|
36
|
+
return this._legacy;
|
|
29
37
|
}
|
|
30
38
|
/**
|
|
31
39
|
* Is this kernel safe? (loaded in an isolated web worker)
|
|
32
40
|
*/
|
|
33
41
|
safeKernel() {
|
|
34
|
-
return
|
|
42
|
+
return !this.isLegacy();
|
|
35
43
|
}
|
|
36
44
|
/**
|
|
37
45
|
* Get a new worker (should be overloaded).
|
|
@@ -39,25 +47,57 @@ export class KernelMainBase extends KernelBase {
|
|
|
39
47
|
newWorker() {
|
|
40
48
|
throw new NotImplementedError("newWorker");
|
|
41
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Import the code for a legacy worker (running in the main thread).
|
|
52
|
+
*/
|
|
53
|
+
async importLegacyWorker() {
|
|
54
|
+
throw new NotImplementedError("importLegacyWorker");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Create the kernel webworker and setup communication.
|
|
58
|
+
*/
|
|
59
|
+
async setupWorker() {
|
|
60
|
+
const comlinkChannel = new MessageChannel();
|
|
61
|
+
if (this.isLegacy()) {
|
|
62
|
+
// running in main thread via message channel (fake worker)
|
|
63
|
+
//@ts-ignore
|
|
64
|
+
self._commPort = comlinkChannel.port1;
|
|
65
|
+
await this.importLegacyWorker();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// setup the kernel worker
|
|
69
|
+
const worker = this.newWorker();
|
|
70
|
+
// send communication port
|
|
71
|
+
worker.postMessage(comlinkChannel.port1, [comlinkChannel.port1]);
|
|
72
|
+
this._worker = worker;
|
|
73
|
+
}
|
|
74
|
+
const KernelWorker = wrap(comlinkChannel.port2);
|
|
75
|
+
// why typescript complains about this call? yet, this is advertised in
|
|
76
|
+
// https://github.com/GoogleChromeLabs/comlink/blob/dffe9050f63b1b39f30213adeb1dd4b9ed7d2594/docs/examples/03-classes-example/index.html#L15
|
|
77
|
+
//@ts-ignore
|
|
78
|
+
this._remote = await new KernelWorker(this._options);
|
|
79
|
+
// setup the communication channel
|
|
80
|
+
const commChannel = new MessageChannel();
|
|
81
|
+
commChannel.port2.onmessage = (e) => this.processWorkerMessage(e.data);
|
|
82
|
+
await this.remote?.setCommPort(transfer(commChannel.port1, [commChannel.port1]));
|
|
83
|
+
// WARNING: without the setCommPort call, KernelWorker (proxy)
|
|
84
|
+
// is garbage collected and comlink releases the proxy,
|
|
85
|
+
// breaking all the communications... (don't know really why)
|
|
86
|
+
// If you experience this, just keep a ref on the proxy with e.g.
|
|
87
|
+
// this.PROXY = KernelWorker
|
|
88
|
+
}
|
|
42
89
|
/**
|
|
43
90
|
* Start the worker.
|
|
44
91
|
*/
|
|
45
92
|
async _start() {
|
|
46
93
|
await super._start();
|
|
47
|
-
const worker = this.newWorker();
|
|
48
94
|
// setup the proxy worker
|
|
49
95
|
const proxyWorker = new Worker(new URL("./comlink-proxy.js", import.meta.url), {
|
|
50
96
|
type: "module",
|
|
51
97
|
});
|
|
52
98
|
const proxy = wrap(proxyWorker);
|
|
53
99
|
// setup the kernel worker
|
|
54
|
-
this.
|
|
55
|
-
worker.onmessage = (e) => this.processWorkerMessage(e.data);
|
|
56
|
-
const KernelWorker = wrap(worker);
|
|
57
|
-
// why typescript complains about this call? yet, this is advertised in
|
|
58
|
-
// https://github.com/GoogleChromeLabs/comlink/blob/dffe9050f63b1b39f30213adeb1dd4b9ed7d2594/docs/examples/03-classes-example/index.html#L15
|
|
59
|
-
//@ts-ignore
|
|
60
|
-
this._remote = await new KernelWorker(this._options);
|
|
100
|
+
await this.setupWorker();
|
|
61
101
|
// bind ports
|
|
62
102
|
const port = await proxy[createEndpoint]();
|
|
63
103
|
await this.remote?.setProxyPort(transfer(port, [port]));
|
|
@@ -14,9 +14,16 @@ type ExecuteResult = {
|
|
|
14
14
|
error?: any;
|
|
15
15
|
};
|
|
16
16
|
declare class KernelWorkerBase {
|
|
17
|
+
protected _options: any;
|
|
18
|
+
protected _legacy: boolean;
|
|
17
19
|
private _ready;
|
|
18
20
|
protected __eval_data__: any;
|
|
21
|
+
protected postMessage?: InstanceType<typeof MessagePort>["postMessage"];
|
|
19
22
|
constructor(options?: any);
|
|
23
|
+
/**
|
|
24
|
+
* Is this a legacy worker? i.e. not runing in a worker.
|
|
25
|
+
*/
|
|
26
|
+
isLegacy(): boolean;
|
|
20
27
|
protected _init(): Promise<void>;
|
|
21
28
|
/**
|
|
22
29
|
* Initialize the kernel. It is called by initWorker in KernelBase.
|
|
@@ -26,6 +33,10 @@ declare class KernelWorkerBase {
|
|
|
26
33
|
* Wait for the kernel to be ready.
|
|
27
34
|
*/
|
|
28
35
|
ready(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Set communication port.
|
|
38
|
+
*/
|
|
39
|
+
setCommPort(port: MessagePort): void;
|
|
29
40
|
/**
|
|
30
41
|
* Set the comlink communication port to proxy fetch.
|
|
31
42
|
*/
|
|
@@ -4,11 +4,13 @@ import { wrap } from "comlink";
|
|
|
4
4
|
class KernelWorkerBase {
|
|
5
5
|
constructor(options) {
|
|
6
6
|
this._ready = new PromiseDelegate();
|
|
7
|
+
this._options = options;
|
|
8
|
+
this._legacy = options?.legacy === true;
|
|
7
9
|
/* we mock the fetch function to:
|
|
8
10
|
* - redirect queries to local FS
|
|
9
11
|
* - bypass the worker's opaque origin
|
|
10
12
|
*/
|
|
11
|
-
mockFetch();
|
|
13
|
+
mockFetch(this.isLegacy());
|
|
12
14
|
// useful for ffi
|
|
13
15
|
self.basthon = this;
|
|
14
16
|
// global prompt redirected to our input
|
|
@@ -16,6 +18,12 @@ class KernelWorkerBase {
|
|
|
16
18
|
self._prompt = globalThis.prompt;
|
|
17
19
|
globalThis.prompt = (prompt) => this.input(prompt);
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Is this a legacy worker? i.e. not runing in a worker.
|
|
23
|
+
*/
|
|
24
|
+
isLegacy() {
|
|
25
|
+
return this._legacy;
|
|
26
|
+
}
|
|
19
27
|
/*
|
|
20
28
|
* Initialize the kernel. Should be overloaded.
|
|
21
29
|
*/
|
|
@@ -38,6 +46,12 @@ class KernelWorkerBase {
|
|
|
38
46
|
ready() {
|
|
39
47
|
return this._ready.promise;
|
|
40
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Set communication port.
|
|
51
|
+
*/
|
|
52
|
+
setCommPort(port) {
|
|
53
|
+
this.postMessage = port.postMessage.bind(port);
|
|
54
|
+
}
|
|
41
55
|
/**
|
|
42
56
|
* Set the comlink communication port to proxy fetch.
|
|
43
57
|
*/
|
|
@@ -48,7 +62,7 @@ class KernelWorkerBase {
|
|
|
48
62
|
* Send stream to stdout/stderr.
|
|
49
63
|
*/
|
|
50
64
|
sendStream(data, stream, text) {
|
|
51
|
-
|
|
65
|
+
this.postMessage?.({
|
|
52
66
|
data,
|
|
53
67
|
type: "stream",
|
|
54
68
|
content: { stream, text },
|
|
@@ -89,7 +103,7 @@ class KernelWorkerBase {
|
|
|
89
103
|
else {
|
|
90
104
|
return;
|
|
91
105
|
}
|
|
92
|
-
|
|
106
|
+
this.postMessage?.({ data, type: "display" });
|
|
93
107
|
}
|
|
94
108
|
/**
|
|
95
109
|
* Display an element like a canvas, ... in the frontend.
|
|
@@ -105,7 +119,7 @@ class KernelWorkerBase {
|
|
|
105
119
|
else {
|
|
106
120
|
data["display_type"] = "multiple";
|
|
107
121
|
data["content"] = element;
|
|
108
|
-
|
|
122
|
+
this.postMessage?.({ data, type: "display" });
|
|
109
123
|
}
|
|
110
124
|
}
|
|
111
125
|
/**
|
|
@@ -170,7 +184,7 @@ class KernelWorkerBase {
|
|
|
170
184
|
* Send a message to the main thread to download a file.
|
|
171
185
|
*/
|
|
172
186
|
download(content, filename) {
|
|
173
|
-
|
|
187
|
+
this.postMessage?.({
|
|
174
188
|
type: "download",
|
|
175
189
|
content: { content, filename },
|
|
176
190
|
});
|
|
@@ -186,7 +200,7 @@ class KernelWorkerBase {
|
|
|
186
200
|
*/
|
|
187
201
|
sleep(duration) {
|
|
188
202
|
duration = duration * 1000; // to milliseconds
|
|
189
|
-
if (globalThis.crossOriginIsolated) {
|
|
203
|
+
if (globalThis.crossOriginIsolated && !this.isLegacy()) {
|
|
190
204
|
const sab = new Int32Array(new SharedArrayBuffer(4));
|
|
191
205
|
Atomics.wait(sab, 0, 0, duration);
|
|
192
206
|
return;
|
|
@@ -226,10 +240,10 @@ class KernelWorkerBase {
|
|
|
226
240
|
* an eval.input event tho the main thread and wait for the response.
|
|
227
241
|
*/
|
|
228
242
|
input(prompt, password = false) {
|
|
229
|
-
if (globalThis.crossOriginIsolated) {
|
|
243
|
+
if (globalThis.crossOriginIsolated && !this.isLegacy()) {
|
|
230
244
|
const data = this.clone(this.__eval_data__);
|
|
231
245
|
const sab = new SharedArrayBuffer(1024);
|
|
232
|
-
|
|
246
|
+
this.postMessage?.({
|
|
233
247
|
type: "input",
|
|
234
248
|
content: { data, prompt, password, sab },
|
|
235
249
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { expose as exposeComlink } from "comlink";
|
|
2
|
+
const insideWorker = () =>
|
|
3
|
+
//@ts-ignore
|
|
4
|
+
typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
|
|
5
|
+
export const expose = (kwc) => {
|
|
6
|
+
if (insideWorker()) {
|
|
7
|
+
// worker
|
|
8
|
+
const listener = (msg) => {
|
|
9
|
+
if (!(msg?.data instanceof MessagePort))
|
|
10
|
+
return;
|
|
11
|
+
removeEventListener("message", listener);
|
|
12
|
+
exposeComlink(kwc, msg.data);
|
|
13
|
+
};
|
|
14
|
+
addEventListener("message", listener);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
// main thread
|
|
18
|
+
exposeComlink(kwc, self._commPort);
|
|
19
|
+
self._commPort = undefined; // no more needed
|
|
20
|
+
}
|
|
21
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basthon/kernel-base",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.0",
|
|
4
4
|
"description": "Basthon - Base Kernel",
|
|
5
5
|
"homepage": "https://basthon.fr",
|
|
6
6
|
"bugs": {
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "bd6fd80041c6cb09cfc5ecbbff3dc69b65974eb8"
|
|
48
48
|
}
|