@tlockney/cdp-kernel 0.4.0 → 0.6.0
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 +78 -478
- package/THIRD-PARTY-NOTICES +62 -0
- package/dist/bun-target.d.ts +2 -2
- package/dist/bun-target.js +30 -8
- package/dist/cdp.d.ts +8 -1
- package/dist/cdp.js +31 -11
- package/dist/celld-target.d.ts +1 -1
- package/dist/celld-target.js +54 -19
- package/dist/children.d.ts +29 -0
- package/dist/children.js +114 -0
- package/dist/debugger.d.ts +1 -1
- package/dist/debugger.js +21 -10
- package/dist/deno-types.d.ts +30 -0
- package/dist/deno-types.js +93 -0
- package/dist/globals.d.ts +3 -3
- package/dist/globals.js +3 -3
- package/dist/kernel.d.ts +7 -1
- package/dist/kernel.js +348 -68
- package/dist/language-service-worker.d.ts +1 -0
- package/dist/language-service-worker.js +88 -0
- package/dist/language-service.d.ts +64 -0
- package/dist/language-service.js +128 -0
- package/dist/main.js +102 -8
- package/dist/mod.d.ts +6 -3
- package/dist/mod.js +6 -3
- package/dist/notebook-types.d.ts +62 -0
- package/dist/notebook-types.js +310 -0
- package/dist/package.d.ts +2 -2
- package/dist/package.js +3 -3
- package/dist/parent.d.ts +12 -0
- package/dist/parent.js +39 -0
- package/dist/port-holder.d.ts +13 -0
- package/dist/port-holder.js +105 -0
- package/dist/sourcemap.d.ts +7 -2
- package/dist/sourcemap.js +9 -3
- package/dist/strip-esbuild.d.ts +2 -0
- package/dist/strip-esbuild.js +44 -0
- package/dist/strip-sucrase.d.ts +2 -0
- package/dist/strip-sucrase.js +78 -0
- package/dist/target.d.ts +58 -6
- package/dist/target.js +412 -54
- package/dist/text.d.ts +5 -1
- package/dist/text.js +55 -2
- package/dist/transform.d.ts +30 -1
- package/dist/transform.js +68 -44
- package/dist/wire.d.ts +8 -1
- package/dist/wire.js +3 -1
- package/dist/workerd-target.d.ts +7 -1
- package/dist/workerd-target.js +102 -8
- package/dist/workers-project.js +13 -2
- package/dist/ws-client.d.ts +5 -1
- package/dist/ws-client.js +46 -13
- package/dist/zmtp.d.ts +9 -1
- package/dist/zmtp.js +42 -1
- package/package.json +17 -6
- package/runtime/bootstrap.mjs +233 -42
- package/runtime/bun-channel.d.mts +3 -0
- package/runtime/bun-channel.mjs +2 -2
- package/runtime/comm.mjs +11 -1
- package/runtime/deno-compat.d.mts +2 -0
- package/runtime/deno-compat.mjs +14 -1
- package/runtime/display.mjs +8 -1
- package/runtime/introspect.mjs +1 -1
- package/runtime/node-hooks.d.mts +48 -0
- package/runtime/node-hooks.mjs +257 -41
- package/runtime/workerd-worker.mjs +77 -6
package/README.md
CHANGED
|
@@ -1,507 +1,107 @@
|
|
|
1
1
|
# cdp-kernel
|
|
2
2
|
|
|
3
|
-
A Jupyter kernel
|
|
4
|
-
|
|
5
|
-
Node or Deno; the code you type runs in a separate **target**
|
|
6
|
-
Node or Deno over
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
A Jupyter kernel for TypeScript and JavaScript, extracted from the one built
|
|
4
|
+
into Deno 2.9.6 (`deno jupyter`), that runs notebooks on several JavaScript runtimes. The kernel
|
|
5
|
+
host runs on Node or Deno; the code you type runs in a separate **target**
|
|
6
|
+
runtime: Node or Deno over the Chrome DevTools Protocol, or Bun, workerd and
|
|
7
|
+
celld, which evaluate cells in-process. In principle, anything else whose
|
|
8
|
+
inspector speaks CDP works too.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
JSR as `@tlockney/cdp-kernel`, but it hasn't seen much real use yet. The
|
|
13
|
-
feasibility study behind it covers the design,
|
|
14
|
-
the other options considered (workerd, Bun, rusty_v8, FFI), and the
|
|
15
|
-
evidence: [read it online](https://claude.ai/artifact/S66TnTmhCzPy2EW7jbeeue)
|
|
16
|
-
(also in the repo as
|
|
17
|
-
[`docs/life-after-deno-jupyter.html`](docs/life-after-deno-jupyter.html)). It
|
|
18
|
-
was written before packaging, so it calls the project a spike.
|
|
19
|
-
The scripts that produced its runtime findings are in [`probes/`](probes/).
|
|
20
|
-
|
|
21
|
-
## Why this shape
|
|
10
|
+
**Website:** <https://cdp-kernel.org/> ·
|
|
11
|
+
**Docs:** [cdp-kernel.org/docs](https://cdp-kernel.org/docs/)
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
[](https://mybinder.org/v2/gh/tlockney/cdp-kernel/main?urlpath=lab/tree/getting-started.ipynb)
|
|
14
|
+
[](https://codespaces.new/tlockney/cdp-kernel?quickstart=1)
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`replMode: true`, `callFunctionOn`, `getProperties`,
|
|
30
|
-
`globalLexicalScopeNames`.
|
|
16
|
+
Binder opens JupyterLab with every runtime and the example notebooks,
|
|
17
|
+
nothing installed. A Codespace does the same in VS Code, with the
|
|
18
|
+
repository checked out ([Install › In a container](https://cdp-kernel.org/docs/install#in-a-container)).
|
|
31
19
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
This is early software. It passes a conformance suite derived from Deno's
|
|
21
|
+
own Jupyter integration tests on every target, and it's packaged for npm and
|
|
22
|
+
JSR as `@tlockney/cdp-kernel`, but it hasn't seen much real use yet.
|
|
23
|
+
The [support policy](https://cdp-kernel.org/docs/support) says which
|
|
24
|
+
runtime versions and frontends are supported, and what may change before
|
|
25
|
+
1.0.
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- The `Deno.jupyter` display API (`jupyter.display`, `md`, `html`, `svg`,
|
|
30
|
+
`image`, …) on every target.
|
|
31
|
+
- TypeScript, TSX, top-level `await`, and `npm:`/`jsr:` imports, with an
|
|
32
|
+
optional lockfile.
|
|
33
|
+
- Deno projects (`deno.json` import maps, JSX settings) and Workers
|
|
34
|
+
projects (`wrangler` bindings in `env`).
|
|
35
|
+
- JupyterLab's visual debugger on Node and Deno targets.
|
|
36
|
+
- anywidget-style widgets and Jupyter comms.
|
|
37
|
+
- `prompt()` and `confirm()`, interrupt that keeps state, Shift-Tab
|
|
38
|
+
inspection, completion and history.
|
|
41
39
|
|
|
42
40
|
## Install
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
`cdp-node-deno` (Node with the Deno compatibility namespace), `cdp-bun`,
|
|
46
|
-
`cdp-workerd`, or `cdp-celld`. Then pick the kernel in JupyterLab or VS Code.
|
|
47
|
-
|
|
48
|
-
**From npm** (host runs on Node 24+). CI tests Node 24 and 26 with Deno 2.9
|
|
49
|
-
and the latest 2.x, on Linux and macOS. Windows isn't supported yet.
|
|
42
|
+
Node 22.18+ host, from npm:
|
|
50
43
|
|
|
51
44
|
```sh
|
|
52
45
|
npm install -g @tlockney/cdp-kernel
|
|
53
|
-
cdp-kernel install --runtime node
|
|
54
|
-
cdp-kernel install --runtime deno
|
|
55
|
-
cdp-kernel install --runtime node --deno-compat
|
|
56
|
-
cdp-kernel install --runtime bun
|
|
57
|
-
cdp-kernel install --runtime workerd
|
|
58
|
-
cdp-kernel install --runtime celld
|
|
46
|
+
cdp-kernel install --runtime node # or deno, bun, workerd, celld
|
|
59
47
|
```
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
package. Re-run `install` after upgrading either.
|
|
63
|
-
|
|
64
|
-
**From JSR** (host runs on Deno):
|
|
49
|
+
Deno host, from JSR:
|
|
65
50
|
|
|
66
51
|
```sh
|
|
67
52
|
deno run -A jsr:@tlockney/cdp-kernel/cli install --runtime deno
|
|
68
53
|
```
|
|
69
54
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
tested). A celld target uses `~/.local/bin/celld`, where celld's installer
|
|
78
|
-
puts it, or the one on `PATH` (0.6.2 is what's been tested; celld has no
|
|
79
|
-
Windows build).
|
|
80
|
-
|
|
81
|
-
Deno 2.9+ refuses package versions less than 24 hours old by default
|
|
82
|
-
([minimum dependency age](https://docs.deno.com/go/minimum-dependency-age)).
|
|
83
|
-
In a release's first day, the install command fails with "blocked by the
|
|
84
|
-
minimum dependency age policy". Pass `--min-dep-age=0` to the install
|
|
85
|
-
command to get it through:
|
|
86
|
-
|
|
87
|
-
```sh
|
|
88
|
-
deno run -A --min-dep-age=0 jsr:@tlockney/cdp-kernel/cli install --runtime deno
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
The kernelspec it writes works on release day too. It relaunches the kernel
|
|
92
|
-
with a dependency cutoff of two days after the release
|
|
93
|
-
(`--min-dep-age=<date>`), so it never resolves anything published after
|
|
94
|
-
that.
|
|
55
|
+
Each install writes a Jupyter kernelspec (`cdp-node`, `cdp-deno`, …). Pick
|
|
56
|
+
it in JupyterLab or VS Code. To run notebooks with no Python installed,
|
|
57
|
+
serve JupyterLab with
|
|
58
|
+
[Ganymede](https://ganymede-server.com/docs/workstation). Linux and macOS
|
|
59
|
+
are supported; Windows isn't yet. See
|
|
60
|
+
[Install](https://cdp-kernel.org/docs/install) for `--deno-compat`, release-day installs on Deno, target runtime
|
|
61
|
+
requirements, and installing from a checkout.
|
|
95
62
|
|
|
96
|
-
|
|
63
|
+
JupyterLab with every runtime and the examples, in a container:
|
|
97
64
|
|
|
98
65
|
```sh
|
|
99
|
-
|
|
100
|
-
npm run install:node # kernelspec cdp-node
|
|
101
|
-
npm run install:deno # kernelspec cdp-deno
|
|
102
|
-
npm run install:node-deno # kernelspec cdp-node-deno
|
|
103
|
-
npm run install:bun # kernelspec cdp-bun
|
|
104
|
-
npm run install:workerd # kernelspec cdp-workerd
|
|
105
|
-
npm run install:celld # kernelspec cdp-celld
|
|
66
|
+
docker run --rm -p 8888:8888 ghcr.io/tlockney/cdp-kernel
|
|
106
67
|
```
|
|
107
68
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
In a cell:
|
|
111
|
-
|
|
112
|
-
- `jupyter.display`, `jupyter.md`, `jupyter.html`, `jupyter.svg`,
|
|
113
|
-
`jupyter.image`, `jupyter.format`, `jupyter.broadcast`, and
|
|
114
|
-
`Symbol.for("Jupyter.display")`: the same API as `Deno.jupyter`.
|
|
115
|
-
- `Deno.jupyter` also works:
|
|
116
|
-
- On a Deno target it is a proxy over the real namespace. Deno's own
|
|
117
|
-
getter throws outside `deno jupyter`.
|
|
118
|
-
- On Node it needs `--deno-compat`. That installs a `Deno` namespace for
|
|
119
|
-
file, env and process APIs (`runtime/deno-compat.mjs`).
|
|
120
|
-
- The shim is opt-in because libraries detect Deno with `typeof Deno`, and
|
|
121
|
-
would take Deno code paths that call APIs it doesn't cover.
|
|
122
|
-
- Imports work: TypeScript, TSX, enums, top-level `await`, and `const`
|
|
123
|
-
redeclaration across cells. Specifiers can be `./local.ts`, bare packages
|
|
124
|
-
from the notebook's `node_modules`, `node:` builtins, and `npm:pkg@range`
|
|
125
|
-
or `jsr:@scope/pkg@range`.
|
|
126
|
-
- On Node, `npm:` and `jsr:` install on first use into
|
|
127
|
-
`~/.cache/cdp-kernel/npm`. An exact version (`npm:react@19.0.0`) installs
|
|
128
|
-
exactly that version. A range (`npm:react@19`) is resolved the first
|
|
129
|
-
time it's imported and stays at that version, even as other packages
|
|
130
|
-
are installed later. Caches written by cdp-kernel 0.3.0 or earlier
|
|
131
|
-
recorded ranges, which later installs could re-resolve. Delete
|
|
132
|
-
`~/.cache/cdp-kernel/npm` to start fresh. `jsr:` works with npm 12,
|
|
133
|
-
which otherwise refuses JSR's registry tarballs.
|
|
134
|
-
- **Lockfile** (cdp-kernel 0.4.0 or later). To pin versions, create a `cdp-kernel.lock.json` in the
|
|
135
|
-
notebook's directory (or above it) containing `{}`. From then on, each
|
|
136
|
-
`npm:`/`jsr:` range is recorded with the exact version it resolved to,
|
|
137
|
-
and installed at that version, so the notebook gets the same packages
|
|
138
|
-
on another machine. Commit it next to the notebook. Deno targets use
|
|
139
|
-
Deno's own resolution and lock instead. Installs into the shared cache
|
|
140
|
-
are serialized, so two kernels installing at once can't corrupt it.
|
|
141
|
-
- On Deno they use Deno's global cache, unless the notebook is in a Deno
|
|
142
|
-
project (below).
|
|
143
|
-
- On Bun they install with `bun add` into `~/.cache/cdp-kernel/bun`. Other
|
|
144
|
-
bare imports resolve from the notebook's directory, so without a
|
|
145
|
-
`node_modules` there, Bun auto-installs them.
|
|
146
|
-
- On workerd, the host bundles every import other than a builtin
|
|
147
|
-
(`node:`, `cloudflare:`) with esbuild, using Workers' resolution
|
|
148
|
-
conditions, and sends it with the cell. `npm:` and `jsr:` install into
|
|
149
|
-
the npm cache first, as on Node.
|
|
150
|
-
- **Deno projects.** If a `deno.json` or `deno.jsonc` sits in the notebook's
|
|
151
|
-
directory or above it, a Deno target uses it, as `deno jupyter` does:
|
|
152
|
-
- its import map (`"@/": "./lib/"` aliases, mapped packages);
|
|
153
|
-
- its `nodeModulesDir` setting, with `package.json` dependencies from the
|
|
154
|
-
project's `node_modules`;
|
|
155
|
-
- its JSX and decorator `compilerOptions`: `jsx` (`react-jsx`,
|
|
156
|
-
`react-jsxdev`, `precompile`, `react`), `jsxImportSource`,
|
|
157
|
-
`jsxFactory`, `jsxFragmentFactory`, `experimentalDecorators`.
|
|
158
|
-
|
|
159
|
-
The import map wins over `node_modules`, as in Deno. The kernel never
|
|
160
|
-
writes to the project's lockfile. A `deno.json` that doesn't parse is
|
|
161
|
-
reported in the first cell's output, and the kernel runs as in a plain
|
|
162
|
-
directory. Outside a project, the target runs with
|
|
163
|
-
`--node-modules-dir=none --no-lock`.
|
|
164
|
-
- `prompt()` and `confirm()` work over the stdin channel, except on
|
|
165
|
-
workerd. Interrupt keeps state, except for a busy cell on Bun or workerd
|
|
166
|
-
(below).
|
|
167
|
-
- **Shift-Tab** shows a tooltip for the name at the cursor: its type, a
|
|
168
|
-
function's signature (and its source at the second press), a preview, and
|
|
169
|
-
its properties. Nothing is run to get it. On Node and Deno the value is
|
|
170
|
-
evaluated with V8's side-effect check (`throwOnSideEffect`), which also
|
|
171
|
-
guards completion. Bun, workerd and celld look the name up by property,
|
|
172
|
-
without calling getters. Executed cells are kept for `history_request`
|
|
173
|
-
(tail, range and search).
|
|
174
|
-
|
|
175
|
-
## Bun
|
|
176
|
-
|
|
177
|
-
Bun's `--inspect` speaks the WebKit Inspector Protocol, which lacks what the
|
|
178
|
-
CDP backend relies on: `replMode`, `Runtime.addBinding`,
|
|
179
|
-
`Runtime.terminateExecution`, and top-level `await` in `Runtime.evaluate`.
|
|
180
|
-
So the Bun target has its own backend (`src/bun-target.ts`,
|
|
181
|
-
`runtime/bun-channel.mjs`):
|
|
182
|
-
|
|
183
|
-
- **No inspector.** The host and Bun exchange JSON lines over a pipe on
|
|
184
|
-
fd 3, which only the host holds, so there is no port to discover.
|
|
185
|
-
- **Cells run in-process.** Each cell becomes global code wrapped in an
|
|
186
|
-
async function (`transformCellForEval` in `src/transform.ts`). Its
|
|
187
|
-
top-level declarations become properties of `globalThis`, so later
|
|
188
|
-
cells, and closures from this one, see them. This is the
|
|
189
|
-
tslab/IJavascript technique.
|
|
190
|
-
- **Same output ordering.** stdout and stderr stay pipes, ordered against
|
|
191
|
-
display messages by the same barrier markers as the CDP targets.
|
|
192
|
-
- **Interrupt.** A cell that's waiting on a promise is interrupted in place,
|
|
193
|
-
keeping state. A busy one (`while (true) {}`) can't be reached, so Bun is
|
|
194
|
-
stopped and restarts on the next cell without the earlier state. The cell
|
|
195
|
-
output says so.
|
|
196
|
-
|
|
197
|
-
What differs from the CDP targets is listed under [Known gaps](#known-gaps).
|
|
198
|
-
|
|
199
|
-
## workerd
|
|
200
|
-
|
|
201
|
-
workerd's inspector can evaluate code, but outside a request there is no
|
|
202
|
-
I/O context: timers, `fetch` and `crypto.randomUUID()` throw "Disallowed
|
|
203
|
-
operation called within global scope". So the workerd target runs its
|
|
204
|
-
cells inside a driver Worker (`src/workerd-target.ts`,
|
|
205
|
-
`runtime/workerd-worker.mjs`):
|
|
206
|
-
|
|
207
|
-
- **One session.** The host opens a single WebSocket to the driver, and
|
|
208
|
-
every cell runs inside that session's request. Cells get Workers'
|
|
209
|
-
`fetch`, `Request`, `caches`, timers and randomness, and anything one
|
|
210
|
-
cell makes carries over to the next: globals, a Durable Object stub, a
|
|
211
|
-
stream, a timer still pending.
|
|
212
|
-
- **A private socket.** Outside a Workers project, the host writes a
|
|
213
|
-
workerd config into a private temporary directory and serves the driver
|
|
214
|
-
on a Unix socket there, opened with a per-launch token. There is no TCP
|
|
215
|
-
port and no inspector.
|
|
216
|
-
- **Cells.** Cells are transformed as for Bun and run by workerd's
|
|
217
|
-
`UnsafeEval` binding (hence `--experimental`).
|
|
218
|
-
- **Output.** `console` output and display messages come back over the
|
|
219
|
-
session as they happen, in order.
|
|
220
|
-
- **Interrupt.** A cell that's waiting on something is abandoned in place,
|
|
221
|
-
keeping state, as on Bun. A busy one gets Bun's treatment too: workerd is
|
|
222
|
-
stopped and restarts on the next cell.
|
|
223
|
-
|
|
224
|
-
What workerd's own process prints, such as its warnings, goes to the Jupyter
|
|
225
|
-
server log, not the notebook.
|
|
226
|
-
|
|
227
|
-
### Workers projects
|
|
228
|
-
|
|
229
|
-
If a `wrangler.jsonc`, `wrangler.json` or `wrangler.toml` sits in the
|
|
230
|
-
notebook's directory or above it, the driver runs on the project's own
|
|
231
|
-
Miniflare, the simulator `wrangler dev` uses, with the project's bindings
|
|
232
|
-
(`src/workers-project.ts`):
|
|
233
|
-
|
|
234
|
-
- `env` holds the bindings: whatever wrangler translates for Miniflare.
|
|
235
|
-
Vars, KV, D1 and Durable Objects are tested; R2, Queues and the others
|
|
236
|
-
go through the same path but aren't.
|
|
237
|
-
- The project's `main` is bundled into the driver, so its Durable Object
|
|
238
|
-
classes run, and `worker` is its default export:
|
|
239
|
-
`await worker.fetch(new Request("https://x/"), env)`.
|
|
240
|
-
- State lives in the project's `.wrangler/state/cdp-kernel`, beside
|
|
241
|
-
`wrangler dev`'s own, so the two never open the same Durable Object.
|
|
242
|
-
- The first cell's output names the bindings. If the project can't load,
|
|
243
|
-
for example because wrangler isn't installed in it, the first cell says
|
|
244
|
-
why and the kernel runs on plain workerd.
|
|
245
|
-
|
|
246
|
-
wrangler and Miniflare come from the project's `node_modules`, so they match
|
|
247
|
-
its versions. Miniflare 4 and 5 both work. Miniflare listens on a loopback
|
|
248
|
-
TCP port, so there the per-launch token is what keeps other local processes
|
|
249
|
-
out.
|
|
250
|
-
|
|
251
|
-
## celld
|
|
252
|
-
|
|
253
|
-
[celld](https://github.com/denoland/celld) runs Workers and Durable Objects
|
|
254
|
-
on your own storage. Like workerd it has no I/O outside a request, so the
|
|
255
|
-
celld target runs the same driver Worker over the same WebSocket session
|
|
256
|
-
(`src/celld-target.ts`). Cells, output, interrupts and host-bundled imports
|
|
257
|
-
all work as on workerd. The differences:
|
|
258
|
-
|
|
259
|
-
- **No `UnsafeEval` needed.** celld allows a plain global `eval`, so it runs
|
|
260
|
-
without workerd's `--experimental`.
|
|
261
|
-
- **One module.** The host bundles the driver, and a project's `main`, into
|
|
262
|
-
a single module, and gives celld the result with `no_bundle`. celld
|
|
263
|
-
doesn't need its own esbuild.
|
|
264
|
-
- **Loopback TCP.** celld listens only on TCP, so the session is a loopback
|
|
265
|
-
WebSocket opened with a per-launch token. The kernel picks a free port
|
|
266
|
-
and runs `celld dev --no-watch` on a generated config.
|
|
267
|
-
- **Projects.** In a celld project (a `wrangler.jsonc` or `wrangler.json`
|
|
268
|
-
at or above the notebook), the generated config copies the project's
|
|
269
|
-
compatibility settings and bindings: vars, KV, D1, R2, Durable Objects,
|
|
270
|
-
Queues, Workflows, services, Worker Loaders. `env` and `worker` then work
|
|
271
|
-
as on workerd. The generated config is written to the project's
|
|
272
|
-
`.celld/cdp-kernel` and removed when the kernel stops. The state stays
|
|
273
|
-
there across kernels, beside `celld dev`'s own `.celld/dev`. celld rejects `wrangler.toml`; a project
|
|
274
|
-
that can't load is reported in the first cell, and the driver runs alone.
|
|
275
|
-
- **Stopping.** `celld dev` runs its node in a separate process group and
|
|
276
|
-
waits for it to stop gracefully, which a busy cell never allows. So an
|
|
277
|
-
interrupt of a busy cell kills the node, then `celld dev`.
|
|
278
|
-
|
|
279
|
-
## Debugger
|
|
280
|
-
|
|
281
|
-
On Node and Deno targets, JupyterLab's visual debugger works: the bug
|
|
282
|
-
button in the notebook toolbar, breakpoints in the cell gutter, the call
|
|
283
|
-
stack, variables and scopes, stepping, pause on exceptions, the debug
|
|
284
|
-
console, and the rich variable viewer. The kernel translates the Jupyter
|
|
285
|
-
debug protocol (DAP over `debug_request`) to V8's `Debugger` domain on the
|
|
286
|
-
session it already holds (`src/debugger.ts`):
|
|
287
|
-
|
|
288
|
-
- **Cell paths.** Each cell is named as JupyterLab names it: a temp-dir
|
|
289
|
-
prefix plus the Murmur2 hash of its code (`src/murmur2.ts`, matched
|
|
290
|
-
against ipykernel's). It's evaluated with that name as its
|
|
291
|
-
`//# sourceURL`, so breakpoints bind to the cell and frames point back at
|
|
292
|
-
it.
|
|
293
|
-
- **Lines.** Breakpoint and frame lines go through the cell's source map
|
|
294
|
-
(`src/sourcemap.ts`), since esbuild rewrites the TypeScript. A breakpoint
|
|
295
|
-
on a blank or type-only line moves to the next statement.
|
|
296
|
-
- **Nothing runs on its own.** The variable explorer between cells reads
|
|
297
|
-
the cells' declarations with V8's side-effect check, so a getter never
|
|
298
|
-
runs. Frames, scopes and the rich viewer evaluate on the paused frame.
|
|
299
|
-
- **Kernelspec.** `metadata.debugger` is set for Node and Deno kernelspecs,
|
|
300
|
-
which is what JupyterLab checks. Reinstall older kernelspecs to get it.
|
|
301
|
-
|
|
302
|
-
`CDPK_DEBUG_TRACE=1` logs every debug request, reply and event to the
|
|
303
|
-
Jupyter server's log.
|
|
304
|
-
|
|
305
|
-
Bun, workerd and celld don't run over an inspector, so they have no
|
|
306
|
-
debugger.
|
|
307
|
-
|
|
308
|
-
## Editor types
|
|
309
|
-
|
|
310
|
-
Cells run untyped, as in `deno jupyter`. Notebook code your editor
|
|
311
|
-
type-checks can declare the globals.
|
|
312
|
-
|
|
313
|
-
**Node notebooks.** The npm package ships the declarations. Add it as a dev
|
|
314
|
-
dependency, then reference it once from a cell or a `.d.ts` in the project:
|
|
315
|
-
|
|
316
|
-
```ts
|
|
317
|
-
/// <reference types="@tlockney/cdp-kernel/globals" />
|
|
318
|
-
await jupyter.display(jupyter.md`typed`);
|
|
319
|
-
const name: string | null = prompt("Name?", "", { password: false });
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
That declares `jupyter`, `prompt` and `confirm`. With the `cdp-node-deno`
|
|
323
|
-
kernel, use `@tlockney/cdp-kernel/globals/deno-compat` instead: it adds the
|
|
324
|
-
`Deno` namespace the compatibility shim implements. A custom display is a
|
|
325
|
-
class with a `[jupyter.$display]()` method returning a MIME bundle.
|
|
326
|
-
`jupyter.$display` is `Symbol.for("Jupyter.display")` at runtime, but only
|
|
327
|
-
`jupyter.$display` carries the precise type.
|
|
328
|
-
|
|
329
|
-
**Deno notebooks.** Use `Deno.jupyter`, which Deno already types and which a
|
|
330
|
-
Deno target provides. The JSR package doesn't ship these globals, because
|
|
331
|
-
JSR doesn't allow packages to augment global types.
|
|
332
|
-
|
|
333
|
-
## Widgets and comms
|
|
334
|
-
|
|
335
|
-
`deno jupyter` has no comms. Here they're supported in both directions, so
|
|
336
|
-
anywidget-style widgets work in frontends with the `anywidget` extension:
|
|
337
|
-
JupyterLab, Notebook 7 and VS Code. Install it with
|
|
338
|
-
`pip install anywidget`; that's the frontend half, no Python kernel needed.
|
|
69
|
+
## Quick start
|
|
339
70
|
|
|
340
71
|
```ts
|
|
341
|
-
|
|
342
|
-
esm: `export default {
|
|
343
|
-
render({ model, el }) {
|
|
344
|
-
const btn = document.createElement("button");
|
|
345
|
-
const update = () => { btn.textContent = \`count is \${model.get("count")}\`; };
|
|
346
|
-
btn.onclick = () => { model.set("count", model.get("count") + 1); model.save_changes(); };
|
|
347
|
-
model.on("change:count", update);
|
|
348
|
-
update();
|
|
349
|
-
el.append(btn);
|
|
350
|
-
},
|
|
351
|
-
};`,
|
|
352
|
-
state: { count: 0 },
|
|
353
|
-
});
|
|
354
|
-
counter.on("change:count", (value) => console.log("clicked to", value));
|
|
355
|
-
counter; // display it
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
The model API:
|
|
359
|
-
|
|
360
|
-
- `get(key)`, and `set(key, value)` or `set({ ... })`, which syncs to every
|
|
361
|
-
view.
|
|
362
|
-
- `on("change:key" | "change" | "msg:custom", listener)` and `off(...)`.
|
|
363
|
-
- `send(content, buffers)` for custom messages.
|
|
364
|
-
- `close()`.
|
|
365
|
-
|
|
366
|
-
`Uint8Array`, `ArrayBuffer` and `DataView` values in the state travel as
|
|
367
|
-
binary buffers. JupyterLab restores widget state after a page reload over
|
|
368
|
-
the `jupyter.widget.control` comm.
|
|
72
|
+
import { format } from "npm:date-fns@4";
|
|
369
73
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
```ts
|
|
374
|
-
const comm = jupyter.comm.open("my.target", { hello: 1 });
|
|
375
|
-
comm.onMsg(({ data, buffers }) => console.log(data, buffers.length));
|
|
376
|
-
comm.send({ ping: true });
|
|
377
|
-
jupyter.comm.registerTarget("echo", (comm) => comm.onMsg((m) => comm.send(m.data)));
|
|
74
|
+
const now: Date = new Date();
|
|
75
|
+
await jupyter.display(jupyter.md`**Today** is ${format(now, "PPPP")}`);
|
|
378
76
|
```
|
|
379
77
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
The
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
CLI: an installed npm bin, or `deno run -A src/main.ts` to test a Deno host.
|
|
411
|
-
|
|
412
|
-
## Releasing
|
|
413
|
-
|
|
414
|
-
One version number lives in three places: `package.json`, `deno.jsonc` and
|
|
415
|
-
`src/package.ts`. `test/package.test.ts` fails if they disagree. To release:
|
|
416
|
-
|
|
417
|
-
1. Bump all three, and set `RELEASE_DATE` in `src/package.ts` to today
|
|
418
|
-
(UTC). Commit, and merge to `main`.
|
|
419
|
-
2. Tag and push: `git tag v0.1.1 && git push origin v0.1.1`.
|
|
420
|
-
|
|
421
|
-
The `publish` workflow then:
|
|
422
|
-
|
|
423
|
-
1. Checks that the tag matches the version, and that `RELEASE_DATE` is
|
|
424
|
-
today or yesterday. A JSR kernelspec's dependency cutoff comes from that
|
|
425
|
-
date, so a stale one would block the release it ships in.
|
|
426
|
-
2. Runs the type check, the unit tests, and the e2e suite against the packed
|
|
427
|
-
npm tarball and against a Deno host.
|
|
428
|
-
3. Publishes to JSR (`deno publish`).
|
|
429
|
-
4. Publishes to npm (`npm publish`).
|
|
430
|
-
|
|
431
|
-
Both registries authenticate the workflow through GitHub OIDC, so no tokens
|
|
432
|
-
are stored:
|
|
433
|
-
- **npm:** a trusted publisher for `tlockney/cdp-kernel` + `publish.yml`.
|
|
434
|
-
- **JSR:** the package is linked to this repo in its JSR settings.
|
|
435
|
-
|
|
436
|
-
Neither registry lets a version be republished. Each job skips a version
|
|
437
|
-
its registry already has, so re-running a release only fills in the half
|
|
438
|
-
that failed.
|
|
439
|
-
|
|
440
|
-
## Known gaps
|
|
441
|
-
|
|
442
|
-
- Widgets are anywidget-style (`jupyter.widget`). The classic ipywidgets
|
|
443
|
-
controls (sliders, dropdowns) are Python classes, so they're not available.
|
|
444
|
-
- Inspection tooltips show what's found at runtime (type, signature,
|
|
445
|
-
preview, properties), not TypeScript types or JSDoc. On Bun, workerd and
|
|
446
|
-
celld, a getter on the path is reported but not called. History is kept
|
|
447
|
-
for the current kernel session only.
|
|
448
|
-
- The debugger is for Node and Deno targets only. Breakpoints are per line,
|
|
449
|
-
conditional breakpoints and logpoints aren't supported, variables can't be
|
|
450
|
-
set from the debugger, and "copy to globals" isn't offered.
|
|
451
|
-
- `npm:` on Node, Bun, workerd and celld:
|
|
452
|
-
- one npm alias per `name@range` (or exact version, when locked);
|
|
453
|
-
- the cache is shared by every notebook, so peer dependencies resolve by
|
|
454
|
-
real name from it;
|
|
455
|
-
- a project's own `package.json` doesn't decide an `npm:` import's version
|
|
456
|
-
(bare imports do use its `node_modules`).
|
|
457
|
-
- No type checking. Deno doesn't type-check REPL cells either.
|
|
458
|
-
- Results are formatted by each runtime's own inspector: `util.inspect` on
|
|
459
|
-
Node, Bun, workerd and celld, and `Deno.inspect` on Deno. On a fixture of
|
|
460
|
-
30 typical values, 23 format identically on Node and Deno. The rest
|
|
461
|
-
differ in two ways:
|
|
462
|
-
- strings are quoted `'…'` on Node and `"…"` on Deno, wherever they
|
|
463
|
-
appear;
|
|
464
|
-
- a displayed `Error`'s stack also lists Node's own module-loader frames.
|
|
465
|
-
|
|
466
|
-
That's deliberate: each runtime's users see the format they expect, and
|
|
467
|
-
Node's inspector has no quote-style option, short of porting Deno's
|
|
468
|
-
formatter. `test/formatting.test.ts` runs both runtimes on the fixture
|
|
469
|
-
and fails if a new kind of difference appears (#17).
|
|
470
|
-
- WebGPU texture and buffer display is dropped.
|
|
471
|
-
- On Bun:
|
|
472
|
-
- Interrupting a busy cell restarts Bun and loses state. An awaiting cell
|
|
473
|
-
keeps it.
|
|
474
|
-
- Top-level declarations are properties of `globalThis`. A `const` can be
|
|
475
|
-
reassigned, and declaring a global's name (`const fetch = ...`) replaces
|
|
476
|
-
that global rather than shadowing it.
|
|
477
|
-
- Only a trailing expression statement shows a result: `x` does, but
|
|
478
|
-
`if (c) { x }` doesn't.
|
|
479
|
-
- Bun 1.4 can't import a module whose path contains `?`.
|
|
480
|
-
- No `--deno-compat` namespace.
|
|
481
|
-
- On workerd:
|
|
482
|
-
- All cells share one request, so it never ends. Unlike production
|
|
483
|
-
Workers, there is no per-request CPU or duration limit, and
|
|
484
|
-
`ctx.waitUntil` isn't available in a cell.
|
|
485
|
-
- No `prompt()` or `confirm()` answers: they return `null`, as without a
|
|
486
|
-
stdin channel. `jupyter.image(path)` can't read files; pass the bytes.
|
|
487
|
-
- Each imported module is bundled with its dependencies. Two imports that
|
|
488
|
-
share a dependency each get their own copy.
|
|
489
|
-
- Interrupting a busy cell restarts workerd and loses state.
|
|
490
|
-
- Top-level declarations and results behave as on Bun: declarations are
|
|
491
|
-
`globalThis` properties, and only a trailing expression shows a result.
|
|
492
|
-
- In a Workers project, `main` is bundled with esbuild directly, not with
|
|
493
|
-
wrangler's build. Custom `build` commands, `alias` and module `rules`
|
|
494
|
-
aren't applied.
|
|
495
|
-
- On celld, as on workerd, except:
|
|
496
|
-
- Only the binding and compatibility keys of a project's config are
|
|
497
|
-
copied. `assets`, `triggers` and `containers` aren't, and neither is
|
|
498
|
-
`.dev.vars`. Relative paths inside the copied keys resolve from
|
|
499
|
-
`.celld/cdp-kernel`, not from the project.
|
|
500
|
-
- Interrupting a busy cell restarts celld and loses state.
|
|
501
|
-
|
|
502
|
-
## Provenance
|
|
503
|
-
|
|
504
|
-
`src/zmtp.ts`, `src/wire.ts`, `src/kernel.ts`, `src/text.ts` and
|
|
505
|
-
`runtime/display.mjs` are ports of Deno's `cli/js/jupyter_kernel.js` and
|
|
506
|
-
`cli/js/40_jupyter.js` at v2.9.6 (commit `e518fbd`), Copyright 2018-2026 the
|
|
507
|
-
Deno authors, MIT license. Each file header says which parts.
|
|
78
|
+
## Documentation
|
|
79
|
+
|
|
80
|
+
New to Jupyter? Start with
|
|
81
|
+
[Getting started](https://cdp-kernel.org/docs/getting-started).
|
|
82
|
+
|
|
83
|
+
- Guides: [Writing notebooks](https://cdp-kernel.org/docs/notebooks),
|
|
84
|
+
[The `jupyter` namespace](https://cdp-kernel.org/docs/jupyter-api),
|
|
85
|
+
[Choosing a runtime](https://cdp-kernel.org/docs/runtimes),
|
|
86
|
+
[Widgets and comms](https://cdp-kernel.org/docs/widgets),
|
|
87
|
+
[Debugger](https://cdp-kernel.org/docs/debugger)
|
|
88
|
+
- Reference: [Editor types](https://cdp-kernel.org/docs/editor-types),
|
|
89
|
+
[Known gaps](https://cdp-kernel.org/docs/known-gaps),
|
|
90
|
+
[Support policy](https://cdp-kernel.org/docs/support)
|
|
91
|
+
- Internals: [Architecture](https://cdp-kernel.org/docs/architecture),
|
|
92
|
+
[Runtime integrations](https://cdp-kernel.org/docs/integrations)
|
|
93
|
+
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md),
|
|
94
|
+
[Development](https://cdp-kernel.org/docs/development),
|
|
95
|
+
[Security policy](SECURITY.md)
|
|
96
|
+
|
|
97
|
+
The [feasibility study](https://cdp-kernel.org/docs/feasibility-study)
|
|
98
|
+
behind the project covers the design, the alternatives considered (workerd,
|
|
99
|
+
Bun, rusty_v8, FFI), and the evidence.
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT. Parts are ports of Deno's Jupyter kernel, Copyright 2018-2026 the Deno
|
|
104
|
+
authors. The debugger's MurmurHash2 follows JupyterLab's (BSD-3-Clause) and
|
|
105
|
+
Gary Court's (MIT); their notices are in
|
|
106
|
+
[THIRD-PARTY-NOTICES](THIRD-PARTY-NOTICES). See
|
|
107
|
+
[Architecture › Provenance](https://cdp-kernel.org/docs/architecture#provenance).
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Third-party notices for cdp-kernel
|
|
2
|
+
|
|
3
|
+
cdp-kernel itself is under the MIT license in LICENSE. The files below carry
|
|
4
|
+
code from other projects under their own licenses.
|
|
5
|
+
|
|
6
|
+
--------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
src/murmur2.ts follows JupyterLab's packages/debugger/src/hash.ts
|
|
9
|
+
(Copyright (c) Jupyter Development Team), which is distributed under the
|
|
10
|
+
following license:
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2015-2026 Project Jupyter Contributors
|
|
13
|
+
All rights reserved.
|
|
14
|
+
|
|
15
|
+
Redistribution and use in source and binary forms, with or without
|
|
16
|
+
modification, are permitted provided that the following conditions are met:
|
|
17
|
+
|
|
18
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
19
|
+
list of conditions and the following disclaimer.
|
|
20
|
+
|
|
21
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
22
|
+
this list of conditions and the following disclaimer in the documentation
|
|
23
|
+
and/or other materials provided with the distribution.
|
|
24
|
+
|
|
25
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
26
|
+
contributors may be used to endorse or promote products derived from
|
|
27
|
+
this software without specific prior written permission.
|
|
28
|
+
|
|
29
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
30
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
31
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
32
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
33
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
34
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
35
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
36
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
37
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
38
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
+
|
|
40
|
+
JupyterLab's hash.ts is in turn adapted from Gary Court's murmurhash2_gc.js
|
|
41
|
+
(https://github.com/garycourt/murmurhash-js), distributed under the following
|
|
42
|
+
license:
|
|
43
|
+
|
|
44
|
+
Copyright (c) 2011 Gary Court
|
|
45
|
+
|
|
46
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
47
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
48
|
+
in the Software without restriction, including without limitation the rights
|
|
49
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
50
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
51
|
+
furnished to do so, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
The above copyright notice and this permission notice shall be included in all
|
|
54
|
+
copies or substantial portions of the Software.
|
|
55
|
+
|
|
56
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
57
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
58
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
59
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
60
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
61
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
62
|
+
SOFTWARE.
|