@tlockney/cdp-kernel 0.7.1 → 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.
@@ -58,9 +58,10 @@ export declare class BrowserTarget implements KernelTarget {
58
58
  evaluate(script: string): Promise<EvaluateResult>;
59
59
  /**
60
60
  * A cell that's waiting on something leaves the Worker free: it answers a
61
- * ping, and the cell is abandoned with its state intact. A busy cell
62
- * can't be reached, so the Worker is terminated, and a new one starts on
63
- * the next cell.
61
+ * ping, and the cell is abandoned with its state intact; host requests
62
+ * in flight are cancelled on the page, and reject in the Worker with an
63
+ * AbortError (#388). A busy cell can't be reached, so the Worker is
64
+ * terminated, and a new one starts on the next cell.
64
65
  */
65
66
  interrupt(): Promise<void>;
66
67
  /** Output arrives before the reply it precedes. */
@@ -107,6 +107,15 @@ export class BrowserTarget {
107
107
  resolve(host({ method: String(msg.method), params: msg.params }, controller.signal));
108
108
  }).then((result) => answer({ result }), (err) => answer({ error: describeHostError(err) }));
109
109
  }
110
+ /** Aborts each host request in flight and rejects it in the Worker. */
111
+ #cancelHostCalls() {
112
+ const calls = [...this.#hostCalls];
113
+ this.#hostCalls.clear();
114
+ for (const [id, controller] of calls) {
115
+ controller.abort();
116
+ this.#worker.postMessage({ op: "host", id, error: { name: "AbortError", message: "interrupted" } });
117
+ }
118
+ }
110
119
  /** Sends `host.onEvent` listeners in the Worker an event (#384). */
111
120
  notifyHost(event, value) {
112
121
  if (!this.#exited)
@@ -154,9 +163,10 @@ export class BrowserTarget {
154
163
  }
155
164
  /**
156
165
  * A cell that's waiting on something leaves the Worker free: it answers a
157
- * ping, and the cell is abandoned with its state intact. A busy cell
158
- * can't be reached, so the Worker is terminated, and a new one starts on
159
- * the next cell.
166
+ * ping, and the cell is abandoned with its state intact; host requests
167
+ * in flight are cancelled on the page, and reject in the Worker with an
168
+ * AbortError (#388). A busy cell can't be reached, so the Worker is
169
+ * terminated, and a new one starts on the next cell.
160
170
  */
161
171
  async interrupt() {
162
172
  this.#interrupted = true;
@@ -171,6 +181,7 @@ export class BrowserTarget {
171
181
  this.#pending.delete(id);
172
182
  this.#interruptible.delete(id);
173
183
  }
184
+ this.#cancelHostCalls();
174
185
  return;
175
186
  }
176
187
  this.#stopNote = RESTART_NOTE;
package/dist/package.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /** Kept equal to package.json and deno.jsonc by test/package.test.ts. */
2
- export declare const VERSION = "0.7.1";
2
+ export declare const VERSION = "0.7.2";
3
3
  /**
4
4
  * The UTC day this version was released, set with VERSION. The publish
5
5
  * workflow refuses a tag unless it's today or yesterday, so it can't go
6
6
  * stale: see dependencyCutoff.
7
7
  */
8
- export declare const RELEASE_DATE = "2026-10-19";
8
+ export declare const RELEASE_DATE = "2026-11-02";
9
9
  /**
10
10
  * Deno blocks packages less than 24 hours old (minimum dependency age), so
11
11
  * a kernelspec relaunching a release from JSR would fail on release day. It
package/dist/package.js CHANGED
@@ -14,13 +14,13 @@ import { join, sep } from "node:path";
14
14
  import process from "node:process";
15
15
  import { fileURLToPath } from "node:url";
16
16
  /** Kept equal to package.json and deno.jsonc by test/package.test.ts. */
17
- export const VERSION = "0.7.1";
17
+ export const VERSION = "0.7.2";
18
18
  /**
19
19
  * The UTC day this version was released, set with VERSION. The publish
20
20
  * workflow refuses a tag unless it's today or yesterday, so it can't go
21
21
  * stale: see dependencyCutoff.
22
22
  */
23
- export const RELEASE_DATE = "2026-10-19";
23
+ export const RELEASE_DATE = "2026-11-02";
24
24
  /**
25
25
  * Deno blocks packages less than 24 hours old (minimum dependency age), so
26
26
  * a kernelspec relaunching a release from JSR would fail on release day. It
@@ -18,6 +18,16 @@ export declare function workerdConfig({ socket, compatibilityDate }: {
18
18
  * otherwise use the one on PATH.
19
19
  */
20
20
  export declare function findWorkerd(cwd: string): string;
21
+ /**
22
+ * The native workerd binary behind `executable`. npm's workerd package
23
+ * installs bin/workerd as a Node script that runs the platform package's
24
+ * binary with execFileSync, and only its postinstall swaps the binary in.
25
+ * When install scripts don't run (the npm that comes with Node 27 blocks
26
+ * those not in allowScripts), killing that script leaves the binary running
27
+ * with the driver socket open, so it's never what the target spawns (#397).
28
+ * Anything that isn't the workerd package's script is returned as given.
29
+ */
30
+ export declare function nativeWorkerd(executable: string): string;
21
31
  /** "workerd 2026-10-15" -> "2026-10-15": the newest date it supports. */
22
32
  export declare function compatibilityDateOf(versionOutput: string): string;
23
33
  /** The literal specifiers a transformed cell imports, in order. */
@@ -18,10 +18,10 @@ import { Buffer } from "node:buffer";
18
18
  import process from "node:process";
19
19
  import { execFileSync, spawn } from "node:child_process";
20
20
  import { randomBytes } from "node:crypto";
21
- import { copyFileSync, existsSync, lstatSync, mkdtempSync, readdirSync, readFileSync, readlinkSync, rmSync, writeFileSync } from "node:fs";
22
- import { builtinModules } from "node:module";
21
+ import { copyFileSync, existsSync, lstatSync, mkdtempSync, readdirSync, readFileSync, readlinkSync, realpathSync, rmSync, writeFileSync } from "node:fs";
22
+ import { builtinModules, createRequire } from "node:module";
23
23
  import { tmpdir } from "node:os";
24
- import { dirname, join } from "node:path";
24
+ import { basename, delimiter, dirname, isAbsolute, join } from "node:path";
25
25
  import { pathToFileURL } from "node:url";
26
26
  import { build } from "esbuild";
27
27
  import { asEvaluateResult } from "./cdp.js";
@@ -88,6 +88,50 @@ export function findWorkerd(cwd) {
88
88
  return "workerd";
89
89
  }
90
90
  }
91
+ /**
92
+ * The native workerd binary behind `executable`. npm's workerd package
93
+ * installs bin/workerd as a Node script that runs the platform package's
94
+ * binary with execFileSync, and only its postinstall swaps the binary in.
95
+ * When install scripts don't run (the npm that comes with Node 27 blocks
96
+ * those not in allowScripts), killing that script leaves the binary running
97
+ * with the driver socket open, so it's never what the target spawns (#397).
98
+ * Anything that isn't the workerd package's script is returned as given.
99
+ */
100
+ export function nativeWorkerd(executable) {
101
+ const candidates = [];
102
+ let path = executable;
103
+ if (!isAbsolute(path) && !path.includes("/") && !path.includes("\\")) {
104
+ const dir = (process.env.PATH ?? "").split(delimiter).find((d) => d && existsSync(join(d, path)));
105
+ if (dir === undefined)
106
+ return executable;
107
+ path = join(dir, path);
108
+ }
109
+ // node_modules/.bin/workerd, a link or (on Windows) a .cmd shim.
110
+ if (basename(dirname(path)) === ".bin")
111
+ candidates.push(join(dirname(dirname(path)), "workerd"));
112
+ try {
113
+ // A link to <package>/bin/workerd, as a global install makes.
114
+ candidates.push(dirname(dirname(realpathSync(path))));
115
+ }
116
+ catch {
117
+ return executable;
118
+ }
119
+ for (const pkg of candidates) {
120
+ try {
121
+ const { name } = JSON.parse(readFileSync(join(pkg, "package.json"), "utf8"));
122
+ if (name !== "workerd")
123
+ continue;
124
+ // The package's main export is the binary's path.
125
+ const binary = createRequire(join(pkg, "package.json"))(join(pkg, "lib", "main.js")).default;
126
+ if (typeof binary === "string" && existsSync(binary))
127
+ return binary;
128
+ }
129
+ catch {
130
+ // Not the npm package, or one this doesn't understand: use it as given.
131
+ }
132
+ }
133
+ return executable;
134
+ }
91
135
  /** "workerd 2026-10-15" -> "2026-10-15": the newest date it supports. */
92
136
  export function compatibilityDateOf(versionOutput) {
93
137
  const m = /\b(\d{4}-\d{2}-\d{2})\b/.exec(versionOutput);
@@ -271,7 +315,7 @@ export class RawWorkerd {
271
315
  this.runtime = runtime;
272
316
  }
273
317
  static async start(cwd, executable) {
274
- const workerd = executable ?? findWorkerd(cwd);
318
+ const workerd = nativeWorkerd(executable ?? findWorkerd(cwd));
275
319
  const compatibilityDate = compatibilityDateOf(execFileSync(workerd, ["--version"], { encoding: "utf8" }));
276
320
  const source = await runtimeDir(new URL("../runtime/", import.meta.url).href);
277
321
  // mkdtemp makes the directory 0700: only this user reaches the socket,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tlockney/cdp-kernel",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Deno's Jupyter kernel, extracted to run notebooks on Node, Deno, Bun, workerd and celld",
5
5
  "license": "MIT",
6
6
  "author": "Thomas Lockney <thomas@lockney.net>",