@basthon/kernel-base 0.62.22 → 0.64.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 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.Basthon.getFile(path);
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.62.22";
1
+ export declare const VERSION = "0.64.0";
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.62.22";
1
+ export const VERSION = "0.64.0";
@@ -1,2 +1,3 @@
1
1
  export * from "./kernel-main";
2
2
  export * from "./kernel-worker";
3
+ export * from "./worker-utils";
@@ -1,2 +1,3 @@
1
1
  export * from "./kernel-main";
2
2
  export * from "./kernel-worker";
3
+ export * from "./worker-utils";
@@ -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.sandboxed) {
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 true;
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._worker = worker;
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
- globalThis.postMessage({
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
- globalThis.postMessage({ data, type: "display" });
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
- globalThis.postMessage({ data, type: "display" });
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
- globalThis.postMessage({
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
- globalThis.postMessage({
246
+ this.postMessage?.({
233
247
  type: "input",
234
248
  content: { data, prompt, password, sab },
235
249
  });
@@ -0,0 +1,6 @@
1
+ declare global {
2
+ interface DedicatedWorkerGlobalScope {
3
+ _commPort?: MessagePort;
4
+ }
5
+ }
6
+ export declare const expose: (kwc: any) => void;
@@ -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.62.22",
3
+ "version": "0.64.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": "1dd6225a7caf14cbee203687cac611495f5b18d4"
47
+ "gitHead": "1e6bb930f8b51e2a307179afcd9426d2672af4e3"
48
48
  }