@tlockney/cdp-kernel 0.7.0 → 0.7.2
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/README.md +4 -2
- package/dist/browser/kernel.js +150 -13
- package/dist/browser/kernel.js.map +2 -2
- package/dist/browser/mod.d.ts +6 -1
- package/dist/browser/mod.js +4 -1
- package/dist/browser/target.d.ts +27 -3
- package/dist/browser/target.js +76 -7
- package/dist/browser/worker.js +83 -12
- package/dist/browser/worker.js.map +3 -3
- package/dist/bun-target.d.ts +2 -0
- package/dist/bun-target.js +15 -2
- package/dist/cdp.d.ts +2 -0
- package/dist/cdp.js +34 -7
- package/dist/celld-target.js +3 -2
- package/dist/debugger.js +51 -5
- package/dist/kernel-core.d.ts +9 -0
- package/dist/kernel-core.js +73 -3
- package/dist/kernel.js +12 -0
- package/dist/main.js +11 -4
- package/dist/package.d.ts +2 -2
- package/dist/package.js +2 -2
- package/dist/target-api.d.ts +12 -0
- package/dist/target-api.js +26 -0
- package/dist/target.d.ts +3 -2
- package/dist/target.js +25 -13
- package/dist/workerd-target.d.ts +16 -1
- package/dist/workerd-target.js +103 -15
- package/dist/workers-project.d.ts +1 -1
- package/dist/workers-project.js +4 -3
- package/package.json +3 -1
- package/runtime/bootstrap.mjs +2 -0
- package/runtime/browser-worker.mjs +83 -0
- package/runtime/evaluator.mjs +2 -0
- package/runtime/workerd-worker.mjs +2 -0
package/README.md
CHANGED
|
@@ -5,7 +5,9 @@ into Deno 2.9.6 (`deno jupyter`), that runs notebooks on several JavaScript runt
|
|
|
5
5
|
host runs on Node or Deno; the code you type runs in a separate **target**
|
|
6
6
|
runtime: Node or Deno over the Chrome DevTools Protocol, or Bun, workerd and
|
|
7
7
|
celld, which evaluate cells in-process. In principle, anything else whose
|
|
8
|
-
inspector speaks CDP works too.
|
|
8
|
+
inspector speaks CDP works too. The kernel can also run inside a web page,
|
|
9
|
+
with cells in a Web Worker and no server
|
|
10
|
+
([The browser](https://cdp-kernel.org/docs/integration-browser), experimental).
|
|
9
11
|
|
|
10
12
|
**Website:** <https://cdp-kernel.org/> ·
|
|
11
13
|
**Docs:** [cdp-kernel.org/docs](https://cdp-kernel.org/docs/)
|
|
@@ -33,7 +35,7 @@ runtime versions and frontends are supported, and what may change before
|
|
|
33
35
|
- Deno projects (`deno.json` import maps, JSX settings) and Workers
|
|
34
36
|
projects (`wrangler` bindings in `env`).
|
|
35
37
|
- JupyterLab's visual debugger on Node and Deno targets.
|
|
36
|
-
- anywidget-style widgets and Jupyter comms.
|
|
38
|
+
- [anywidget](https://github.com/manzt/anywidget)-style widgets and Jupyter comms.
|
|
37
39
|
- `prompt()` and `confirm()`, interrupt that keeps state, Shift-Tab
|
|
38
40
|
inspection, completion and history.
|
|
39
41
|
|
package/dist/browser/kernel.js
CHANGED
|
@@ -28401,6 +28401,17 @@ function makeHeader(session, msgType) {
|
|
|
28401
28401
|
}
|
|
28402
28402
|
|
|
28403
28403
|
// src/target-api.ts
|
|
28404
|
+
function parseBundle(text) {
|
|
28405
|
+
const bundle = typeof text === "string" ? parseJson(text) : void 0;
|
|
28406
|
+
return typeof bundle === "object" && bundle !== null && !Array.isArray(bundle) ? bundle : {};
|
|
28407
|
+
}
|
|
28408
|
+
function parseJson(text) {
|
|
28409
|
+
try {
|
|
28410
|
+
return JSON.parse(text);
|
|
28411
|
+
} catch {
|
|
28412
|
+
return void 0;
|
|
28413
|
+
}
|
|
28414
|
+
}
|
|
28404
28415
|
function isTargetMessage(value) {
|
|
28405
28416
|
if (typeof value !== "object" || value === null) return false;
|
|
28406
28417
|
const kind = value.kind;
|
|
@@ -28746,6 +28757,7 @@ function clip(frames) {
|
|
|
28746
28757
|
}
|
|
28747
28758
|
|
|
28748
28759
|
// src/kernel-core.ts
|
|
28760
|
+
var TYPESCRIPT_VERSION = "6";
|
|
28749
28761
|
function fromBase64(text) {
|
|
28750
28762
|
return Uint8Array.from(atob(text), (c) => c.charCodeAt(0));
|
|
28751
28763
|
}
|
|
@@ -28814,6 +28826,8 @@ var KernelCore = class {
|
|
|
28814
28826
|
#input = null;
|
|
28815
28827
|
/** An interrupt is waiting on the target. */
|
|
28816
28828
|
#interrupting = false;
|
|
28829
|
+
/** Ends the cell waiting for the target to start, while one is (#336). */
|
|
28830
|
+
#interruptStart = null;
|
|
28817
28831
|
/** The shell message being handled, and since when. */
|
|
28818
28832
|
#handling = null;
|
|
28819
28833
|
// Open comms, for comm_info_request: comm_id -> target_name (#9).
|
|
@@ -28932,6 +28946,21 @@ var KernelCore = class {
|
|
|
28932
28946
|
});
|
|
28933
28947
|
return this.#targetStarting;
|
|
28934
28948
|
}
|
|
28949
|
+
/**
|
|
28950
|
+
* The target for a cell. An interrupt while it starts ends the cell; the
|
|
28951
|
+
* start goes on, bounded by the target's own deadline, for the next one (#336).
|
|
28952
|
+
*/
|
|
28953
|
+
async #targetForCell() {
|
|
28954
|
+
if (this.#target) return this.#target;
|
|
28955
|
+
const interrupted = new Promise((_, reject) => {
|
|
28956
|
+
this.#interruptStart = () => reject(new Interrupted("Execution interrupted"));
|
|
28957
|
+
});
|
|
28958
|
+
try {
|
|
28959
|
+
return await Promise.race([this.#ensureTarget(), interrupted]);
|
|
28960
|
+
} finally {
|
|
28961
|
+
this.#interruptStart = null;
|
|
28962
|
+
}
|
|
28963
|
+
}
|
|
28935
28964
|
#targetEvents() {
|
|
28936
28965
|
return {
|
|
28937
28966
|
stream: (name, text) => this.#publish("stream", { name, text }),
|
|
@@ -28974,6 +29003,10 @@ ${text}
|
|
|
28974
29003
|
input2.settle({ value: null, interrupted: true });
|
|
28975
29004
|
return;
|
|
28976
29005
|
}
|
|
29006
|
+
if (this.#interruptStart !== null) {
|
|
29007
|
+
this.#interruptStart();
|
|
29008
|
+
return;
|
|
29009
|
+
}
|
|
28977
29010
|
if (!this.#target || !this.#executing) return;
|
|
28978
29011
|
this.#interrupting = true;
|
|
28979
29012
|
try {
|
|
@@ -29029,7 +29062,7 @@ ${text}
|
|
|
29029
29062
|
implementation_version: this.#host.version,
|
|
29030
29063
|
language_info: {
|
|
29031
29064
|
name: "typescript",
|
|
29032
|
-
version:
|
|
29065
|
+
version: this.#host.languageVersion ?? TYPESCRIPT_VERSION,
|
|
29033
29066
|
mimetype: "text/x.typescript",
|
|
29034
29067
|
file_extension: ".ts",
|
|
29035
29068
|
pygments_lexer: "typescript",
|
|
@@ -29042,7 +29075,8 @@ ${text}
|
|
|
29042
29075
|
debugger: this.#debugger !== null,
|
|
29043
29076
|
// What current ipykernel advertises; JupyterLab itself goes by the
|
|
29044
29077
|
// kernelspec's metadata.debugger.
|
|
29045
|
-
supported_features: this.#debugger !== null ? ["debugger"] : []
|
|
29078
|
+
supported_features: this.#debugger !== null ? ["debugger"] : [],
|
|
29079
|
+
...this.#host.capabilities ? { capabilities: this.#host.capabilities } : {}
|
|
29046
29080
|
};
|
|
29047
29081
|
}
|
|
29048
29082
|
// --- control channel ------------------------------------------------------
|
|
@@ -29281,6 +29315,39 @@ ${withoutSignature(runtime)}`;
|
|
|
29281
29315
|
}
|
|
29282
29316
|
return target.evaluate(transform2(code));
|
|
29283
29317
|
}
|
|
29318
|
+
/** execute_request's user_expressions, each as its value's MIME bundle or the error it threw (#340). */
|
|
29319
|
+
async #userExpressions(target, expressions) {
|
|
29320
|
+
const results = {};
|
|
29321
|
+
if (typeof expressions !== "object" || expressions === null) return results;
|
|
29322
|
+
for (const [name, expression] of Object.entries(expressions)) {
|
|
29323
|
+
results[name] = await this.#userExpression(target, String(expression));
|
|
29324
|
+
}
|
|
29325
|
+
return results;
|
|
29326
|
+
}
|
|
29327
|
+
async #userExpression(target, expression) {
|
|
29328
|
+
try {
|
|
29329
|
+
const source = `(
|
|
29330
|
+
${expression}
|
|
29331
|
+
)`;
|
|
29332
|
+
const result = await target.evaluate(`${target.transform(source, this.#transformOptions())}
|
|
29333
|
+
//# sourceURL=$cdpk$user-expression.ts`);
|
|
29334
|
+
if (result.exceptionDetails) {
|
|
29335
|
+
const exception = result.exceptionDetails.exception;
|
|
29336
|
+
const described = exception ? await target.describeError(exception) : { name: "Error", message: result.exceptionDetails.text, stack: "" };
|
|
29337
|
+
return {
|
|
29338
|
+
status: "error",
|
|
29339
|
+
ename: described.name || "Error",
|
|
29340
|
+
evalue: described.message,
|
|
29341
|
+
traceback: (described.stack || `${described.name}: ${described.message}`).split("\n")
|
|
29342
|
+
};
|
|
29343
|
+
}
|
|
29344
|
+
return { status: "ok", data: await target.format(result.result), metadata: {} };
|
|
29345
|
+
} catch (err) {
|
|
29346
|
+
const ename = err instanceof CellSyntaxError ? "SyntaxError" : err instanceof Interrupted ? "Interrupted" : "Error";
|
|
29347
|
+
const evalue = err instanceof Error ? err.message : String(err);
|
|
29348
|
+
return { status: "error", ename, evalue, traceback: [`${ename}: ${evalue}`] };
|
|
29349
|
+
}
|
|
29350
|
+
}
|
|
29284
29351
|
async #execute(msg, peer) {
|
|
29285
29352
|
const parent = msg.header;
|
|
29286
29353
|
if (this.#aborting) {
|
|
@@ -29306,10 +29373,11 @@ ${withoutSignature(runtime)}`;
|
|
|
29306
29373
|
this.#projectWarning = null;
|
|
29307
29374
|
}
|
|
29308
29375
|
let error = null;
|
|
29376
|
+
let userExpressions = {};
|
|
29309
29377
|
let structured = null;
|
|
29310
29378
|
this.#executing = true;
|
|
29311
29379
|
try {
|
|
29312
|
-
const target = await this.#
|
|
29380
|
+
const target = await this.#targetForCell();
|
|
29313
29381
|
if (target.notice !== void 0 && this.#noticeShownFor !== target) {
|
|
29314
29382
|
this.#noticeShownFor = target;
|
|
29315
29383
|
this.#publish("stream", { name: "stderr", text: `${target.notice}
|
|
@@ -29334,6 +29402,7 @@ ${withoutSignature(runtime)}`;
|
|
|
29334
29402
|
} else if (!silent && result.result.type !== "undefined") {
|
|
29335
29403
|
await target.broadcastResult(count, result.result);
|
|
29336
29404
|
}
|
|
29405
|
+
if (!result.exceptionDetails) userExpressions = await this.#userExpressions(target, msg.content.user_expressions);
|
|
29337
29406
|
await target.flush();
|
|
29338
29407
|
} catch (err) {
|
|
29339
29408
|
if (err instanceof CellSyntaxError) {
|
|
@@ -29362,7 +29431,7 @@ ${withoutSignature(runtime)}`;
|
|
|
29362
29431
|
status: "ok",
|
|
29363
29432
|
execution_count: count,
|
|
29364
29433
|
payload: [],
|
|
29365
|
-
user_expressions:
|
|
29434
|
+
user_expressions: userExpressions
|
|
29366
29435
|
});
|
|
29367
29436
|
}
|
|
29368
29437
|
this.#status("idle", parent);
|
|
@@ -29370,7 +29439,7 @@ ${withoutSignature(runtime)}`;
|
|
|
29370
29439
|
};
|
|
29371
29440
|
|
|
29372
29441
|
// stub:package
|
|
29373
|
-
var VERSION = "0.7.
|
|
29442
|
+
var VERSION = "0.7.2";
|
|
29374
29443
|
|
|
29375
29444
|
// src/browser/channels.ts
|
|
29376
29445
|
var PEER = "page";
|
|
@@ -29485,13 +29554,17 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29485
29554
|
#pending = /* @__PURE__ */ new Map();
|
|
29486
29555
|
/** Requests that interrupt() abandons when the driver can still answer. */
|
|
29487
29556
|
#interruptible = /* @__PURE__ */ new Set();
|
|
29557
|
+
/** Host requests in flight, by the Worker's id (#384). */
|
|
29558
|
+
#hostCalls = /* @__PURE__ */ new Map();
|
|
29559
|
+
#host;
|
|
29488
29560
|
#nextId = 0;
|
|
29489
29561
|
#interrupted = false;
|
|
29490
29562
|
#stopNote;
|
|
29491
29563
|
#exited = false;
|
|
29492
|
-
constructor(worker, events) {
|
|
29564
|
+
constructor(worker, events, host) {
|
|
29493
29565
|
this.#worker = worker;
|
|
29494
29566
|
this.#events = events;
|
|
29567
|
+
this.#host = host;
|
|
29495
29568
|
worker.addEventListener("message", (event) => this.#receive(event.data));
|
|
29496
29569
|
worker.addEventListener("error", (event) => {
|
|
29497
29570
|
this.#events.stream("stderr", `[cdp-kernel] browser Worker error: ${event.message ?? "unknown"}
|
|
@@ -29500,10 +29573,10 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29500
29573
|
});
|
|
29501
29574
|
}
|
|
29502
29575
|
static async launch(events, options) {
|
|
29503
|
-
const target = new _BrowserTarget(options.createWorker(), events);
|
|
29576
|
+
const target = new _BrowserTarget(options.createWorker(), events, options.host);
|
|
29504
29577
|
const base2 = options.base ?? globalThis.location?.href ?? "file:///";
|
|
29505
29578
|
try {
|
|
29506
|
-
const { userAgent } = await target.#request("info", { base: base2, cdn: options.cdn });
|
|
29579
|
+
const { userAgent } = await target.#request("info", { base: base2, cdn: options.cdn, preload: options.preload ?? [] });
|
|
29507
29580
|
if (typeof userAgent === "string") target.versions.runtime = browserName(userAgent);
|
|
29508
29581
|
target.versions.engine = engineName(typeof userAgent === "string" ? userAgent : "");
|
|
29509
29582
|
} catch (err) {
|
|
@@ -29515,6 +29588,8 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29515
29588
|
#receive(msg) {
|
|
29516
29589
|
if (isRecord(msg) && msg.kind === "stream") {
|
|
29517
29590
|
this.#events.stream(msg.name === "stderr" ? "stderr" : "stdout", String(msg.text));
|
|
29591
|
+
} else if (isRecord(msg) && msg.kind === "host") {
|
|
29592
|
+
this.#hostRequest(msg);
|
|
29518
29593
|
} else if (isTargetMessage(msg)) {
|
|
29519
29594
|
this.#events.message(msg);
|
|
29520
29595
|
} else if (isRecord(msg) && msg.kind === "reply") {
|
|
@@ -29527,10 +29602,52 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29527
29602
|
else pending.resolve(msg);
|
|
29528
29603
|
}
|
|
29529
29604
|
}
|
|
29605
|
+
#hostRequest(msg) {
|
|
29606
|
+
const id = Number(msg.id);
|
|
29607
|
+
if (msg.cancel === true) {
|
|
29608
|
+
this.#hostCalls.get(id)?.abort();
|
|
29609
|
+
this.#hostCalls.delete(id);
|
|
29610
|
+
return;
|
|
29611
|
+
}
|
|
29612
|
+
const controller = new AbortController();
|
|
29613
|
+
this.#hostCalls.set(id, controller);
|
|
29614
|
+
const answer = (reply) => {
|
|
29615
|
+
if (this.#hostCalls.get(id) !== controller) return;
|
|
29616
|
+
this.#hostCalls.delete(id);
|
|
29617
|
+
try {
|
|
29618
|
+
this.#worker.postMessage({ op: "host", id, ...reply });
|
|
29619
|
+
} catch (err) {
|
|
29620
|
+
this.#worker.postMessage({ op: "host", id, error: describeHostError(err) });
|
|
29621
|
+
}
|
|
29622
|
+
};
|
|
29623
|
+
const host = this.#host;
|
|
29624
|
+
new Promise((resolve) => {
|
|
29625
|
+
if (!host) throw new Error("this page offers no host requests");
|
|
29626
|
+
resolve(host({ method: String(msg.method), params: msg.params }, controller.signal));
|
|
29627
|
+
}).then(
|
|
29628
|
+
(result) => answer({ result }),
|
|
29629
|
+
(err) => answer({ error: describeHostError(err) })
|
|
29630
|
+
);
|
|
29631
|
+
}
|
|
29632
|
+
/** Aborts each host request in flight and rejects it in the Worker. */
|
|
29633
|
+
#cancelHostCalls() {
|
|
29634
|
+
const calls = [...this.#hostCalls];
|
|
29635
|
+
this.#hostCalls.clear();
|
|
29636
|
+
for (const [id, controller] of calls) {
|
|
29637
|
+
controller.abort();
|
|
29638
|
+
this.#worker.postMessage({ op: "host", id, error: { name: "AbortError", message: "interrupted" } });
|
|
29639
|
+
}
|
|
29640
|
+
}
|
|
29641
|
+
/** Sends `host.onEvent` listeners in the Worker an event (#384). */
|
|
29642
|
+
notifyHost(event, value) {
|
|
29643
|
+
if (!this.#exited) this.#worker.postMessage({ op: "host", event, value });
|
|
29644
|
+
}
|
|
29530
29645
|
#exit(code) {
|
|
29531
29646
|
if (this.#exited) return;
|
|
29532
29647
|
this.#exited = true;
|
|
29533
29648
|
this.#worker.terminate();
|
|
29649
|
+
for (const controller of this.#hostCalls.values()) controller.abort();
|
|
29650
|
+
this.#hostCalls.clear();
|
|
29534
29651
|
this.#events.exit(code, this.#stopNote);
|
|
29535
29652
|
for (const pending of this.#pending.values()) pending.reject(new Abandoned("the browser Worker stopped"));
|
|
29536
29653
|
this.#pending.clear();
|
|
@@ -29558,9 +29675,10 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29558
29675
|
}
|
|
29559
29676
|
/**
|
|
29560
29677
|
* A cell that's waiting on something leaves the Worker free: it answers a
|
|
29561
|
-
* ping, and the cell is abandoned with its state intact
|
|
29562
|
-
*
|
|
29563
|
-
* the
|
|
29678
|
+
* ping, and the cell is abandoned with its state intact; host requests
|
|
29679
|
+
* in flight are cancelled on the page, and reject in the Worker with an
|
|
29680
|
+
* AbortError (#388). A busy cell can't be reached, so the Worker is
|
|
29681
|
+
* terminated, and a new one starts on the next cell.
|
|
29564
29682
|
*/
|
|
29565
29683
|
async interrupt() {
|
|
29566
29684
|
this.#interrupted = true;
|
|
@@ -29575,6 +29693,7 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29575
29693
|
this.#pending.delete(id);
|
|
29576
29694
|
this.#interruptible.delete(id);
|
|
29577
29695
|
}
|
|
29696
|
+
this.#cancelHostCalls();
|
|
29578
29697
|
return;
|
|
29579
29698
|
}
|
|
29580
29699
|
this.#stopNote = RESTART_NOTE;
|
|
@@ -29596,6 +29715,10 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29596
29715
|
}
|
|
29597
29716
|
return { handled: reply.handled === true };
|
|
29598
29717
|
}
|
|
29718
|
+
async format(value) {
|
|
29719
|
+
const reply = await this.#request("format", { handle: value.objectId });
|
|
29720
|
+
return parseBundle(reply.value);
|
|
29721
|
+
}
|
|
29599
29722
|
async describeError(exception) {
|
|
29600
29723
|
if (exception.objectId === void 0) return { name: "Error", message: exception.description ?? "", stack: "" };
|
|
29601
29724
|
const { value } = await this.#request("describe", { handle: exception.objectId });
|
|
@@ -29621,6 +29744,17 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29621
29744
|
this.#exit(null);
|
|
29622
29745
|
}
|
|
29623
29746
|
};
|
|
29747
|
+
function describeHostError(err) {
|
|
29748
|
+
if (isRecord(err)) {
|
|
29749
|
+
const { name, message, code } = err;
|
|
29750
|
+
return {
|
|
29751
|
+
name: typeof name === "string" ? name : "Error",
|
|
29752
|
+
message: typeof message === "string" ? message : String(err),
|
|
29753
|
+
...code === void 0 ? {} : { code }
|
|
29754
|
+
};
|
|
29755
|
+
}
|
|
29756
|
+
return { name: "Error", message: String(err) };
|
|
29757
|
+
}
|
|
29624
29758
|
function toBase64(bytes2) {
|
|
29625
29759
|
let binary = "";
|
|
29626
29760
|
for (let i = 0; i < bytes2.length; i += 32768) binary += String.fromCharCode(...bytes2.subarray(i, i + 32768));
|
|
@@ -29651,12 +29785,14 @@ function startBrowserKernel(options) {
|
|
|
29651
29785
|
const log = options.log ?? ((text) => console.debug(`[cdp-kernel] ${text}`));
|
|
29652
29786
|
let closed;
|
|
29653
29787
|
const closedPromise = new Promise((resolve) => closed = resolve);
|
|
29788
|
+
let target = null;
|
|
29654
29789
|
const core = new KernelCore({
|
|
29655
29790
|
runtime: "browser",
|
|
29656
29791
|
version: VERSION,
|
|
29657
29792
|
channels,
|
|
29658
|
-
launchTarget: (events) => BrowserTarget.launch(events, options),
|
|
29793
|
+
launchTarget: async (events) => target = await BrowserTarget.launch(events, options),
|
|
29659
29794
|
typeStrip: options.typeStrip ?? "sucrase",
|
|
29795
|
+
capabilities: options.capabilities,
|
|
29660
29796
|
log,
|
|
29661
29797
|
exit: () => closed()
|
|
29662
29798
|
});
|
|
@@ -29664,7 +29800,8 @@ function startBrowserKernel(options) {
|
|
|
29664
29800
|
return {
|
|
29665
29801
|
send: (channel, msg) => channels.deliver(channel, msg),
|
|
29666
29802
|
onMessage: (listener) => channels.onMessage(listener),
|
|
29667
|
-
closed: closedPromise
|
|
29803
|
+
closed: closedPromise,
|
|
29804
|
+
notifyHost: (event, value) => target?.notifyHost(event, value)
|
|
29668
29805
|
};
|
|
29669
29806
|
}
|
|
29670
29807
|
export {
|