@tlockney/cdp-kernel 0.7.0 → 0.7.1
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 +136 -10
- 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 +23 -0
- package/dist/browser/target.js +62 -4
- 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 +1 -1
- package/dist/package.js +1 -1
- 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 +6 -1
- package/dist/workerd-target.js +56 -12
- 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.1";
|
|
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,43 @@ 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
|
+
/** Sends `host.onEvent` listeners in the Worker an event (#384). */
|
|
29633
|
+
notifyHost(event, value) {
|
|
29634
|
+
if (!this.#exited) this.#worker.postMessage({ op: "host", event, value });
|
|
29635
|
+
}
|
|
29530
29636
|
#exit(code) {
|
|
29531
29637
|
if (this.#exited) return;
|
|
29532
29638
|
this.#exited = true;
|
|
29533
29639
|
this.#worker.terminate();
|
|
29640
|
+
for (const controller of this.#hostCalls.values()) controller.abort();
|
|
29641
|
+
this.#hostCalls.clear();
|
|
29534
29642
|
this.#events.exit(code, this.#stopNote);
|
|
29535
29643
|
for (const pending of this.#pending.values()) pending.reject(new Abandoned("the browser Worker stopped"));
|
|
29536
29644
|
this.#pending.clear();
|
|
@@ -29596,6 +29704,10 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29596
29704
|
}
|
|
29597
29705
|
return { handled: reply.handled === true };
|
|
29598
29706
|
}
|
|
29707
|
+
async format(value) {
|
|
29708
|
+
const reply = await this.#request("format", { handle: value.objectId });
|
|
29709
|
+
return parseBundle(reply.value);
|
|
29710
|
+
}
|
|
29599
29711
|
async describeError(exception) {
|
|
29600
29712
|
if (exception.objectId === void 0) return { name: "Error", message: exception.description ?? "", stack: "" };
|
|
29601
29713
|
const { value } = await this.#request("describe", { handle: exception.objectId });
|
|
@@ -29621,6 +29733,17 @@ var BrowserTarget = class _BrowserTarget {
|
|
|
29621
29733
|
this.#exit(null);
|
|
29622
29734
|
}
|
|
29623
29735
|
};
|
|
29736
|
+
function describeHostError(err) {
|
|
29737
|
+
if (isRecord(err)) {
|
|
29738
|
+
const { name, message, code } = err;
|
|
29739
|
+
return {
|
|
29740
|
+
name: typeof name === "string" ? name : "Error",
|
|
29741
|
+
message: typeof message === "string" ? message : String(err),
|
|
29742
|
+
...code === void 0 ? {} : { code }
|
|
29743
|
+
};
|
|
29744
|
+
}
|
|
29745
|
+
return { name: "Error", message: String(err) };
|
|
29746
|
+
}
|
|
29624
29747
|
function toBase64(bytes2) {
|
|
29625
29748
|
let binary = "";
|
|
29626
29749
|
for (let i = 0; i < bytes2.length; i += 32768) binary += String.fromCharCode(...bytes2.subarray(i, i + 32768));
|
|
@@ -29651,12 +29774,14 @@ function startBrowserKernel(options) {
|
|
|
29651
29774
|
const log = options.log ?? ((text) => console.debug(`[cdp-kernel] ${text}`));
|
|
29652
29775
|
let closed;
|
|
29653
29776
|
const closedPromise = new Promise((resolve) => closed = resolve);
|
|
29777
|
+
let target = null;
|
|
29654
29778
|
const core = new KernelCore({
|
|
29655
29779
|
runtime: "browser",
|
|
29656
29780
|
version: VERSION,
|
|
29657
29781
|
channels,
|
|
29658
|
-
launchTarget: (events) => BrowserTarget.launch(events, options),
|
|
29782
|
+
launchTarget: async (events) => target = await BrowserTarget.launch(events, options),
|
|
29659
29783
|
typeStrip: options.typeStrip ?? "sucrase",
|
|
29784
|
+
capabilities: options.capabilities,
|
|
29660
29785
|
log,
|
|
29661
29786
|
exit: () => closed()
|
|
29662
29787
|
});
|
|
@@ -29664,7 +29789,8 @@ function startBrowserKernel(options) {
|
|
|
29664
29789
|
return {
|
|
29665
29790
|
send: (channel, msg) => channels.deliver(channel, msg),
|
|
29666
29791
|
onMessage: (listener) => channels.onMessage(listener),
|
|
29667
|
-
closed: closedPromise
|
|
29792
|
+
closed: closedPromise,
|
|
29793
|
+
notifyHost: (event, value) => target?.notifyHost(event, value)
|
|
29668
29794
|
};
|
|
29669
29795
|
}
|
|
29670
29796
|
export {
|