Skip to content

ReferenceLook things up as you need them.

Changelog ​

What changed in each release of @tlockney/cdp-kernel (npm and JSR). Issue and pull request numbers refer to tlockney/cdp-kernel.

Unreleased ​

  • An interrupt on the browser target cancels the host requests in flight: the page's handler sees its AbortSignal abort, and the Worker's host.request rejects with an AbortError. Before, a cell waiting on a request ended, but the page never learned the call was dead, so a native dialog it opened stayed up (#388).

0.7.1 (2026-10-19) ​

  • The browser target can offer cells a page's own features: preload modules run in the Worker before the first cell and after each restart, and get a host whose requests the page answers with its host handler, with cancellation through an AbortSignal. A capabilities option is returned in kernel_info_reply, and notifyHost sends the Worker events (#384).
  • The debugger no longer stops on an exception thrown in Deno's or Node's own built-in code (ext: and node: scripts), as Chrome's ignore list does. With "Caught Exceptions" on, Deno's lazy setup of node:process threw and caught one before the cell's own exception and could leave the cell waiting. Stepping also skips over that code (#380).
  • Bun, workerd and celld targets that never become ready are stopped after 60 s, as Node and Deno targets already were, and the first cell reports the failure instead of waiting forever. An interrupt while a target is starting now ends the cell waiting for it (#336).
  • execute_request's user_expressions are evaluated after a cell that succeeds, and execute_reply returns each one's MIME bundle, or the error it threw, as ipykernel does. They were ignored before.
  • kernel_info_reply's language_info.version is the installed TypeScript's version instead of "5" (#340).
  • Starting a workerd kernel no longer trusts other users' entries in the temp directory when it cleans up after dead kernels: it skips any cdpk-workerd-* directory that isn't a private one of this user's, so a planted owner.json can't make it kill another process (on macOS) or stop the kernel starting. A cleanup that fails is ignored (#335).
  • cdp-kernel install --name accepts only the names uninstall does (letters, digits, ., _ and -, not . or ..), so a name with a path separator can't write kernel.json outside the kernels directory (#338).
  • A message from a Node or Deno inspector, or from the workerd or celld driver, that isn't JSON no longer crashes the kernel: the target's pending calls fail and it restarts on the next cell (#337).
  • The JupyterLab debugger writes its cell files only into a temp directory that is the kernel user's own and closed to others. If another user made cdp-kernel_<pid> first, the kernel skips the files (the frontend has the cells anyway) instead of following symlinks planted there, and leaves that directory alone at exit (#339).

0.7.0 (2026-10-19) ​

  • Experimental: the kernel runs in a web page, with cells in a Web Worker and npm: imports from esm.sh. scripts/build-browser.mjs bundles it; The browser says what works. The kernel's message handling moved to src/kernel-core.ts, which has no Node imports; the kernel process is unchanged.
  • The npm package exports the browser kernel: @tlockney/cdp-kernel/browser (startBrowserKernel, typed) and @tlockney/cdp-kernel/browser/worker (the cells' Worker script), built by scripts/build-browser.mjs during npm run build. Not on JSR yet (#333).
  • A cell's error traceback points at cells as In[n] and at the line you wrote, TypeScript types included, instead of <anonymous> and the line of the stripped JavaScript; that holds for a function an earlier cell defined. The error message's metadata also carries the traceback as structured frames (file, line, function, source lines around the failing one, the cell's execution count and cellId, and a library flag), under nteract's application/vnd.nteract.traceback+json. Cells now run under a //# sourceURL of $cdpk$cell-<hash>.ts, which is what a stack read in code shows (#322).
  • A debug console expression that throws before its first await (a typo is enough) no longer occasionally fails with "Promise was collected" on Deno. The expression now yields one task before running, as cells do (#329).
  • Vega and Vega-Lite charts come with a text/plain summary: the mark, each encoded field with its type and value range, the title and the row count, with a line per layer, view or facet panel. Frontends that can't draw the chart (jupyter console, nbconvert to text, agents reading the notebook) now show something useful. DataFrames get the same: row and column counts and each column's type (#321).
  • The acquisition scrubber in the MoMA example notebooks renders in JupyterLab. Its widget module exported a function taking { model, el }, which anywidget calls as a factory with no arguments, so it failed with "Cannot destructure property 'model' of 'undefined'". The docs site's widget replay now treats a default-exported function the same way (#327).
  • The landing page has a "Notebooks without Python" section linking to Ganymede, a Rust Jupyter server that serves JupyterLab with no Python installed (#325).
  • CI runs the browser target's test (test/browser.test.ts) in Playwright's Chromium, WebKit and Firefox; until now it was skipped everywhere. CDPK_BROWSER picks the engine when running it locally (#319).
  • With the debugger's "caught exceptions" filter on, a debug console expression that throws (a typo is enough) no longer pauses the target in the kernel's own call. On Node the request hung until Continue; on Deno the pause ended by itself and the runtime then ran no timers or immediates, so every later cell hung. The kernel's own inspector calls (debug console, rich variable view, Shift-Tab, error descriptions) now run with exception pauses held off (#307).
  • Releases are cut by starting the publish workflow on main with a version; it tags the commit once CI passes, then publishes. The release workflow is gone: the publish run it started had github-actions[bot] as its actor, which JSR refused (#315).
  • Notebook output on the docs site reads in dark mode: the MoMA example's wall labels set their own text color, SVG charts sit on a light panel, and the scrubber widget's year labels follow the page's text color (#312).

0.6.0 (2026-10-18) ​

  • Node and Deno targets no longer keep the data of every comm message from the frontend, so dragging a widget's slider for a while doesn't grow the runtime. V8 kept each message as compiled source; messages now reach the target over its stdin (#287).
  • Node and Deno targets no longer keep every cell's result and thrown error in memory. The inspector held each one it returned until released, so a session that showed large values (arrays, images) grew without bound; the kernel now releases them once the cell's output is shown (#286).
  • The kernel starts its runtime only after it has bound all five ports from the connection file. A Deno runtime binds its inspector to a random port within milliseconds of starting and could take one of them, so the kernel died at startup with EADDRINUSE (#257).
  • Deno's "Debugger session started." notice no longer shows up in the first cell's output when it arrives late (#281).
  • Cells on Node and Deno that show output (a result, display(), console.log) no longer wait ~40 ms for the inspector socket: a trivial cell takes 7 ms instead of 48 ms on Node. Messages from the target now travel on its stdout, and the session that runs cells receives no inspector events (#271).
  • A Node or Deno runtime could stop running setTimeout callbacks for good, leaving the kernel waiting on a cell or comm message forever. A kernel request that arrived while the runtime's own timer code ran could make the runtime lose its 1 ms timer list. Cells and comm handlers now yield with setImmediate instead (#237).
  • Completion knows declared types: TypeScript's language service runs over the executed cells plus the one being edited, so x. lists a typed value's members even before it has a value, completion works inside expressions ([1, 2].map((n) => n.toF), and npm:/jsr: imports complete from their declarations. Runtime-only names are still listed after the typed ones, and JupyterLab shows each match's kind. The service runs in a worker thread started on the first completion (that first request is answered from the runtime alone) and ended after 10 minutes without one; it takes about 150 MB while running. Node targets get @types/node from the notebook's project, or installed once into the per-user npm cache. --no-language-service turns it off. typescript is now a dependency (#273).
  • Shift-Tab inspection shows the declared type, signature and JSDoc from the same language service, then the runtime's value preview and properties. It also works on names that have no value yet and on members of expressions the runtime can't evaluate (#274).
  • Typed completion and inspection know each target's globals: deno types on Deno, @types/bun on Bun, and on workerd and celld a Workers project's worker-configuration.d.ts (from wrangler types), else @cloudflare/workers-types. On Deno, imports an executed cell made (npm:, jsr:, URLs, the project's import map) are typed from Deno's own cache, found with deno info; nothing new is fetched for completion. On Bun, npm:/jsr: types come from Bun's install cache (#275).
  • The docs describe typed completion and inspection, where types come from, their cost and limits, and --no-language-service (#276).
  • Node 22.18+ is supported as the host and as a target; engines was >=24. 22.18 is the first Node 22 that strips TypeScript types without a flag, which imported .ts files rely on. CI adds Node 22 on Linux and macOS. The one difference found: Node 22 formats symbol keys as { [Symbol(k)]: 1 } (#264).
  • kernel_info_request on shell is answered without waiting for the runtime to start, so clients probing during a slow start get one reply instead of queuing more. Until the runtime is up, the banner leaves out its versions. interrupt_reply now waits at most 1 s for the runtime, under the 5 s clients allow control requests (#253).
  • The kernel exits, stopping its runtime, when the server named by JPY_PARENT_PID exits, as ipykernel does, so a server that is killed or crashes no longer leaves kernels running. Without the variable (a kernel started by hand) nothing changes; on Windows it is ignored (#254).
  • cdp-kernel list and cdp-kernel uninstall [NAME...] [--all] list and remove the kernelspecs cdp-kernel installed, without Jupyter. They use the same kernels directory as install and leave other kernels' kernelspecs alone (#249).
  • --type-strip sucrase strips cells' types with sucrase, plain JavaScript, instead of esbuild's native binary, and an embedder can pass its own type stripper. sucrase is an optional dependency, loaded only when picked; it rejects decorators, accessor fields and namespaces (#234, #238).
  • A kernel that is SIGKILLed no longer leaves runtimes behind: a helper process started with the kernel kills the runtimes it registered, and removes their temp directories, when the kernel dies. This covers celld and a Node or Deno runtime busy in a cell (#240), and Miniflare's workerd (#241).
  • When the kernel can't bind a connection-file port, it writes what holds the port (sockets and their processes, from /proc on Linux) to stderr (#257).
  • The debugger moves a breakpoint off a line the type stripper blanked to the next line that runs (#239), and esbuild is loaded only when a cell needs it (#238).
  • A container image with JupyterLab and every runtime (#267), published to ghcr.io/tlockney/cdp-kernel with each release (#269), a devcontainer built from it (#268), and Binder and Codespaces links (#270).
  • Docs: Ganymede as a way to run notebooks without Python (#250), what VS Code needs to draw widgets (#228), the support policy and 1.0 checklist (#261), and CONTRIBUTING.md, SECURITY.md and issue templates (#262).
  • CI runs the tests nightly against the latest runtime releases (#259), adds a soak test of one long session per runtime (#260), and benchmarks startup and per-cell latency (#265). Releases can be cut from the Actions tab with the release workflow (#235).

0.5.0 (2026-10-16) ​

Changes in behavior:

  • The npm and Bun package caches follow XDG_CACHE_HOME, as the runtime cache already did. With it set, packages are downloaded again into the new location, and ~/.cache/cdp-kernel/{npm,bun} can be deleted (#163).
  • On Bun, console output is formatted as on Node, and stdout and stderr writes are synchronous, so a large write before prompt() no longer deadlocks (#180).
  • stop_on_error is honoured: execute requests queued behind a failed cell are aborted instead of run (#123).
  • process.exit() or Deno.exit() in a cell ends the runtime, and the next cell starts a fresh one (#130).

New:

  • On Node, .ts, .tsx and .jsx files imported by a cell are compiled with esbuild when Node's type stripping can't handle them (enums, namespaces, parameter properties, JSX) (#73, #91).
  • Cells can use standard decorators and accessor fields (#142), anonymous export default class/function and import x = require() (#143).
  • --deno-compat throws a clear error for a Deno member it doesn't implement (#172).
  • Documentation site at cdp-kernel.org/docs, with getting started, runtime, troubleshooting, security, VS Code and CLI reference pages, and this changelog.

Jupyter protocol:

  • Interrupting a cell blocked in prompt() makes prompt() throw instead of wedging the kernel, and a stale input_reply no longer answers the next prompt (#121).
  • Malformed JSON from a cell or the stdin port no longer crashes the kernel (#122); a shell handler that throws replies with an error (#125).
  • A silent request publishes no execute_input, a shell shutdown_request is handled, and control requests publish busy/idle (#124).
  • A frontend-opened comm whose comm_open handler throws is dropped (#137).

Fixes:

  • Runtimes no longer outlive the kernel: they are killed when it exits by SIGTERM, SIGHUP, an uncaught error or shutdown during startup (#127, #218); Node and Deno targets exit when their kernel is SIGKILLed (#219); workerd processes and directories left by a dead kernel are cleaned up (#220); workerd is SIGKILLed when SIGTERM doesn't stop it (#128).
  • Deadlocks: prompt() after a large write on Node (#131), a display payload JSON.stringify rejects (#129), and requests on a closed session (#133). An interrupt during result formatting reports Interrupted (#135).
  • Fewer startup failures: a runtime whose start fails or stalls is stopped (#52); intermittent "Promise was collected" errors are fixed (#50, #55); celld retries a start that loses a port race; a kernel port briefly in use at startup is retried (#160); a start whose bootstrap never installs fails with an error (#134).
  • Cell results: a trailing sequence expression returns its last value on Bun, workerd and celld (#140); a trailing side-effect or type-only import is not the result (#139); stack-trace line numbers no longer shift (#150); the completeness check handles escaped backticks and regex literals (#126); .ts/.tsx imports with a query or fragment are compiled (#145).
  • workerd and celld: a module that threw while loading is re-sent (#147); errors from timers and rejections after a cell are reported (#148); npm:/jsr: installs no longer block heartbeat and interrupt (#149).
  • Deno target: Deno. completes and Shift+Tab inspects Deno.* again (#132).
  • cdp-kernel.lock.json is written atomically, a live install lock is no longer stolen, and a corrupt lockfile names its path (#144).
  • Runtime stderr notices are filtered per line, so user lines starting with "Download" are kept (#136); WebSocket fragmented binary messages and Close frames are handled (#138); cleanup of buffers, debugger files, token comparison and image errors (#152).
  • npm installs run through cmd.exe on Windows (#51). Windows is still not supported.
  • LICENSE credits the MurmurHash2 port's JupyterLab and Gary Court licenses (#154).
  • CI tests Deno 2.x (latest) only, pins Bun 1.4, and smoke-tests the published JSR package after each release (#146).

0.4.0 (2026-10-15) ​

  • JupyterLab's visual debugger on Node and Deno: breakpoints, stepping, variables and pause on exceptions, over CDP (#18).
  • Inspection tooltips (Shift+Tab) and input history (#10).
  • An opt-in cdp-kernel.lock.json pins npm:/jsr: versions per notebook, and installs are serialized (#14).
  • npm: installs save exact versions, so a range stays at what it first resolved to, and jsr: works with npm 12 (#39, #40). Caches written by 0.3.0 or earlier can be deleted to start fresh.
  • JSR kernelspecs start on release day despite Deno's minimum dependency age (#12).
  • CI covers Node 24 and 26 with Deno 2.9 and the latest 2.x, on Linux and macOS (#15).
  • A MoMA collection example notebook.

0.3.0 (2026-10-14) ​

  • New targets: Bun (#7), workerd (#8) and celld (#25).
  • In a Workers project, cells get the project's bindings as env (#26).

0.2.0 (2026-10-14) ​

  • Jupyter comms and anywidget-style widgets with jupyter.widget (#9).
  • Deno targets honour the notebook's Deno project config (#13).
  • Type declarations for the jupyter global (#11).
  • kernel_info reports the real version, and the inspector's URL is kept private (#22).

0.1.1 (2026-10-14) ​

First release on npm and JSR. A Jupyter kernel ported from deno jupyter (v2.9.6) that runs cells on Node or Deno over the Chrome DevTools Protocol: the jupyter display API, TypeScript, npm: and jsr: imports, interrupt, prompt()/confirm(), and an opt-in Deno namespace for Node.

MIT license. Parts are ports of Deno's Jupyter kernel, © the Deno authors.