Skip to content

Commit 9e564a7

Browse files
committed
fixes
1 parent 386cd25 commit 9e564a7

7 files changed

Lines changed: 93 additions & 51 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pip uninstall jupyterlite-javascript-kernel
3434
The extension currently registers two JavaScript kernelspecs:
3535

3636
- `JavaScript`:
37-
Runs code in a sandboxed `iframe`. Use this when your code needs browser DOM APIs like `document`, `window`, or canvas access through the page context.
37+
Runs code in a hidden runtime `iframe` on the main page thread. Use this when your code needs browser DOM APIs like `document`, `window`, or canvas access through the page context.
3838
- `JavaScript (Worker)`:
3939
Runs code in a dedicated Web Worker. Use this for stronger isolation and to avoid blocking the main UI thread.
4040

packages/javascript-kernel-extension/src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ const kernelIFrame: JupyterFrontEndPlugin<void> = {
7070
autoStart: true,
7171
requires: [IKernelSpecs],
7272
activate: (app: JupyterFrontEnd, kernelspecs: IKernelSpecs) => {
73-
void app;
7473
registerKernel(kernelspecs, {
7574
name: 'javascript',
7675
displayName: 'JavaScript',
@@ -87,7 +86,6 @@ const kernelWorker: JupyterFrontEndPlugin<void> = {
8786
autoStart: true,
8887
requires: [IKernelSpecs],
8988
activate: (app: JupyterFrontEnd, kernelspecs: IKernelSpecs) => {
90-
void app;
9189
registerKernel(kernelspecs, {
9290
name: 'javascript-worker',
9391
displayName: 'JavaScript (Worker)',
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) Jupyter Development Team.
2+
// Distributed under the terms of the Modified BSD License.
3+
4+
type ErrorLike = {
5+
name?: unknown;
6+
message?: unknown;
7+
stack?: unknown;
8+
};
9+
10+
/**
11+
* Normalize unknown thrown values into Error instances.
12+
*
13+
* Supports cross-realm Error objects (for example iframe-thrown errors)
14+
* by preserving their name/message/stack fields even when `instanceof Error`
15+
* is false in the current realm.
16+
*/
17+
export function normalizeError(error: unknown, fallbackName = 'Error'): Error {
18+
if (error instanceof Error) {
19+
return error;
20+
}
21+
22+
if (isErrorLike(error)) {
23+
const normalized = new Error(
24+
typeof error.message === 'string' ? error.message : safeToString(error)
25+
);
26+
normalized.name =
27+
typeof error.name === 'string' && error.name ? error.name : fallbackName;
28+
29+
if (typeof error.stack === 'string' && error.stack.length > 0) {
30+
normalized.stack = error.stack;
31+
}
32+
33+
return normalized;
34+
}
35+
36+
const normalized = new Error(safeToString(error));
37+
normalized.name = fallbackName;
38+
return normalized;
39+
}
40+
41+
function isErrorLike(error: unknown): error is ErrorLike {
42+
return (
43+
typeof error === 'object' &&
44+
error !== null &&
45+
('name' in error || 'message' in error || 'stack' in error)
46+
);
47+
}
48+
49+
function safeToString(value: unknown): string {
50+
try {
51+
return String(value);
52+
} catch {
53+
return 'Unknown error';
54+
}
55+
}

packages/javascript-kernel/src/kernel.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { KernelMessage } from '@jupyterlab/services';
66
import { BaseKernel, type IKernel } from '@jupyterlite/services';
77

88
import type { JavaScriptExecutor } from './executor';
9+
import { normalizeError as normalizeUnknownError } from './errors';
910
import {
1011
IFrameRuntimeBackend,
1112
IRuntimeBackend,
@@ -196,28 +197,28 @@ export class JavaScriptKernel extends BaseKernel implements IKernel {
196197
* Send an `input_reply` message.
197198
*/
198199
inputReply(content: KernelMessage.IInputReplyMsg['content']): void {
199-
throw new Error('Not implemented');
200+
this._logUnsupportedControlMessage('input_reply');
200201
}
201202

202203
/**
203204
* Send an `comm_open` message.
204205
*/
205206
async commOpen(msg: KernelMessage.ICommOpenMsg): Promise<void> {
206-
throw new Error('Not implemented');
207+
this._logUnsupportedControlMessage('comm_open', msg.content.target_name);
207208
}
208209

209210
/**
210211
* Send an `comm_msg` message.
211212
*/
212213
async commMsg(msg: KernelMessage.ICommMsgMsg): Promise<void> {
213-
throw new Error('Not implemented');
214+
this._logUnsupportedControlMessage('comm_msg');
214215
}
215216

216217
/**
217218
* Send an `comm_close` message.
218219
*/
219220
async commClose(msg: KernelMessage.ICommCloseMsg): Promise<void> {
220-
throw new Error('Not implemented');
221+
this._logUnsupportedControlMessage('comm_close');
221222
}
222223

223224
/**
@@ -248,7 +249,7 @@ export class JavaScriptKernel extends BaseKernel implements IKernel {
248249
execute: async code => {
249250
const reply = await context.execute(code);
250251
if (reply.status === 'error') {
251-
throw this.createRuntimeInitializationError(reply);
252+
throw this._createRuntimeInitializationError(reply);
252253
}
253254
return reply;
254255
}
@@ -308,17 +309,13 @@ export class JavaScriptKernel extends BaseKernel implements IKernel {
308309
* Normalize unknown thrown values into Error instances.
309310
*/
310311
protected normalizeError(error: unknown): Error {
311-
if (error instanceof Error) {
312-
return error;
313-
}
314-
315-
return new Error(String(error));
312+
return normalizeUnknownError(error, 'RuntimeError');
316313
}
317314

318315
/**
319316
* Normalize an execute reply error into an Error instance.
320317
*/
321-
private createRuntimeInitializationError(
318+
private _createRuntimeInitializationError(
322319
reply: KernelMessage.IExecuteReplyMsg['content']
323320
): Error {
324321
const ename =
@@ -343,6 +340,28 @@ export class JavaScriptKernel extends BaseKernel implements IKernel {
343340
return error;
344341
}
345342

343+
/**
344+
* Warn once per unsupported control message type to avoid noisy consoles.
345+
*/
346+
private _logUnsupportedControlMessage(
347+
type: 'input_reply' | 'comm_open' | 'comm_msg' | 'comm_close',
348+
detail?: string
349+
): void {
350+
if (this._unsupportedControlMessages.has(type)) {
351+
return;
352+
}
353+
354+
this._unsupportedControlMessages.add(type);
355+
const suffix = detail ? ` (${detail})` : '';
356+
357+
console.warn(
358+
`[javascript-kernel] Ignoring unsupported ${type} message${suffix}.`
359+
);
360+
}
361+
362+
private _unsupportedControlMessages = new Set<
363+
'input_reply' | 'comm_open' | 'comm_msg' | 'comm_close'
364+
>();
346365
private _backend: IRuntimeBackend;
347366
private _executorFactory?: JavaScriptKernel.IExecutorFactory;
348367
private _runtimeMode: RuntimeMode;

packages/javascript-kernel/src/runtime_backends.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { KernelMessage } from '@jupyterlab/services';
66
import { PromiseDelegate } from '@lumino/coreutils';
77

88
import type { JavaScriptExecutor } from './executor';
9+
import { normalizeError } from './errors';
910
import { JavaScriptRuntimeEvaluator } from './runtime_evaluator';
1011
import type {
1112
RuntimeOutputHandler,
@@ -169,13 +170,11 @@ export class IFrameRuntimeBackend implements IRuntimeBackend {
169170
private async _init(): Promise<void> {
170171
try {
171172
this._container = document.createElement('div');
172-
this._container.style.cssText =
173-
'position:absolute;width:0;height:0;overflow:hidden;';
173+
this._container.style.display = 'none';
174174
document.body.appendChild(this._container);
175175

176176
this._iframe = document.createElement('iframe');
177-
this._iframe.sandbox.add('allow-scripts', 'allow-same-origin');
178-
this._iframe.style.cssText = 'border:none;width:100%;height:100%;';
177+
this._iframe.style.border = 'none';
179178
this._iframe.srcdoc = `<!DOCTYPE html>
180179
<html>
181180
<head>
@@ -500,7 +499,7 @@ export class WorkerRuntimeBackend implements IRuntimeBackend {
500499
});
501500
this._ready.resolve();
502501
} catch (error) {
503-
this._handleWorkerFatal(this._normalizeError(error));
502+
this._handleWorkerFatal(normalizeError(error));
504503
}
505504
}
506505

@@ -524,16 +523,6 @@ export class WorkerRuntimeBackend implements IRuntimeBackend {
524523
this._pending.clear();
525524
}
526525

527-
/**
528-
* Normalize unknown values to Error instances.
529-
*/
530-
private _normalizeError(error: unknown): Error {
531-
if (error instanceof Error) {
532-
return error;
533-
}
534-
return new Error(String(error));
535-
}
536-
537526
private _options: WorkerRuntimeBackend.IOptions;
538527
private _worker: Worker | null = null;
539528
private _readyHandled = false;

packages/javascript-kernel/src/runtime_evaluator.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import type { KernelMessage } from '@jupyterlab/services';
55

66
import { JavaScriptExecutor } from './executor';
7+
import { normalizeError } from './errors';
78
import type { RuntimeOutputHandler } from './runtime_protocol';
89

910
/**
@@ -82,7 +83,7 @@ export class JavaScriptRuntimeEvaluator {
8283
user_expressions: {}
8384
};
8485
} catch (error) {
85-
const normalized = this._normalizeError(error);
86+
const normalized = normalizeError(error);
8687
const cleanedStack = this._executor.cleanStackTrace(normalized);
8788

8889
const content: KernelMessage.IReplyErrorContent = {
@@ -260,17 +261,6 @@ export class JavaScriptRuntimeEvaluator {
260261
this._globalScope.display = this._previousDisplay;
261262
}
262263

263-
/**
264-
* Normalize thrown value to an Error instance.
265-
*/
266-
private _normalizeError(error: unknown): Error {
267-
if (error instanceof Error) {
268-
return error;
269-
}
270-
271-
return new Error(String(error));
272-
}
273-
274264
private _globalScope: Record<string, any>;
275265
private _onOutput: RuntimeOutputHandler;
276266
private _executor: JavaScriptExecutor;

packages/javascript-kernel/src/worker-runtime.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Jupyter Development Team.
22
// Distributed under the terms of the Modified BSD License.
33

4+
import { normalizeError } from './errors';
45
import { JavaScriptRuntimeEvaluator } from './runtime_evaluator';
56
import type {
67
RuntimeRequest,
@@ -163,13 +164,3 @@ function sanitize(value: any, seen: WeakSet<object>, depth: number): any {
163164

164165
return String(value);
165166
}
166-
167-
/**
168-
* Normalize unknown thrown value into Error.
169-
*/
170-
function normalizeError(error: unknown): Error {
171-
if (error instanceof Error) {
172-
return error;
173-
}
174-
return new Error(String(error));
175-
}

0 commit comments

Comments
 (0)