@tlockney/cdp-kernel 0.8.0 → 0.9.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/dist/globals.d.ts CHANGED
@@ -16,7 +16,7 @@ import type { Displayable, DisplayOptions, JupyterNamespace, MediaBundle } from
16
16
  export type { Displayable, DisplayOptions, JupyterNamespace, MediaBundle };
17
17
  /** Options for the notebook's `prompt()`. */
18
18
  export interface PromptOptions {
19
- /** Ask the frontend to hide what is typed. */
19
+ /** Ask the frontend to hide what is typed, for secrets. Default `false`. */
20
20
  password?: boolean;
21
21
  }
22
22
  declare global {
@@ -27,10 +27,41 @@ declare global {
27
27
  */
28
28
  var jupyter: JupyterNamespace;
29
29
  /**
30
- * Ask the frontend for a line of input over the Jupyter stdin channel.
31
- * Returns `null` when the frontend didn't allow input for this cell.
30
+ * Ask for a line of input in the notebook: the frontend shows an input
31
+ * box under the cell, and the cell waits for the answer.
32
+ *
33
+ * @param message The question. Default `"Prompt"`. A non-empty
34
+ * `defaultValue` is shown after it, as `message [default]`.
35
+ * @param defaultValue Returned when the answer is empty. Default `""`.
36
+ * @param options `password: true` hides what is typed.
37
+ * @returns The typed text, `defaultValue` for an empty answer, or `null`
38
+ * when nothing can answer (the frontend allows no input, as in
39
+ * `nbconvert --execute`).
40
+ * @availability On workerd and celld it always returns `null`.
41
+ * @example
42
+ * ```ts
43
+ * const name = prompt("What's your name?", "world");
44
+ * `Hello, ${name}!`;
45
+ * ```
46
+ * @example
47
+ * ```ts
48
+ * const apiKey = prompt("API key", "", { password: true });
49
+ * ```
50
+ * @see jupyter-api.md#asking-for-input-prompt-and-confirm
32
51
  */
33
52
  function prompt(message?: string, defaultValue?: string, options?: PromptOptions): string | null;
34
- /** Ask a yes/no question over the Jupyter stdin channel. */
53
+ /**
54
+ * Ask a yes/no question in the notebook. `[y/N]` is added to the message.
55
+ *
56
+ * @param message The question. Default `"Confirm"`.
57
+ * @returns `true` only for `y` or `Y`; `false` otherwise, and when
58
+ * nothing can answer.
59
+ * @availability On workerd and celld it always returns `false`.
60
+ * @example
61
+ * ```ts
62
+ * if (confirm("Delete the cache?")) console.log("deleting");
63
+ * ```
64
+ * @see jupyter-api.md#asking-for-input-prompt-and-confirm
65
+ */
35
66
  function confirm(message?: string): boolean;
36
67
  }
@@ -160,6 +160,9 @@ export class KernelCore {
160
160
  // Transforms are synchronous, so the stripper loads before any cell.
161
161
  if (typeof this.#typeStrip === "string")
162
162
  await loadTypeStripper(this.#typeStrip);
163
+ // No shell or control reply until every port is bound: a client that
164
+ // has kernel_info_reply goes on to the heartbeat port (#438).
165
+ await this.#channels.ready;
163
166
  await Promise.all([this.#readShell(), this.#runShell(this.#mainShell),
164
167
  this.#loop("control", (m, p) => this.#handleControl(m, p)),
165
168
  this.#loop("stdin", (m) => this.#handleStdin(m))]);
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.8.0";
2
+ export declare const VERSION = "0.9.0";
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-11-06";
8
+ export declare const RELEASE_DATE = "2026-11-09";
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.8.0";
17
+ export const VERSION = "0.9.0";
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-11-06";
23
+ export const RELEASE_DATE = "2026-11-09";
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
@@ -51,7 +51,16 @@ export declare function isTypeStripperName(value: unknown): value is TypeStrippe
51
51
  * imported (#238).
52
52
  */
53
53
  export declare function loadTypeStripper(name: TypeStripperName): Promise<TypeStripper>;
54
- /** Rewrites module-only syntax in plain JavaScript into script syntax. */
54
+ /**
55
+ * Rewrites module-only syntax in plain JavaScript into script syntax.
56
+ *
57
+ * Top-level `const` becomes `let`, as do import bindings (#444). Under
58
+ * replMode a later evaluation may redeclare a top-level name only with the
59
+ * same kind of declaration: `const a` then `let a`, or `const a` then
60
+ * `import { a }`, fails with "already been declared" until a restart. With
61
+ * one kind, cells can be re-run and edited freely; the cost is that
62
+ * assigning to a top-level `const` from another cell doesn't throw.
63
+ */
55
64
  export declare function rewriteModuleSyntax(js: string): string;
56
65
  export declare function transformCell(code: string, options?: CellTransformOptions): string;
57
66
  /**
package/dist/transform.js CHANGED
@@ -113,7 +113,9 @@ function rewriteImport(code, node, index) {
113
113
  return `${local} = ${ns}`;
114
114
  return `${local} = ${ns}[${JSON.stringify(importedName(spec))}]`;
115
115
  });
116
- return `var ${ns} = ${load}; var ${bindings.join(", ")};`;
116
+ // `let`, like top-level `const` (see toLet): replMode lets a later
117
+ // cell redeclare a name only with the same kind of declaration.
118
+ return `var ${ns} = ${load}; let ${bindings.join(", ")};`;
117
119
  }
118
120
  function parseCell(js) {
119
121
  try {
@@ -128,10 +130,26 @@ function parseCell(js) {
128
130
  throw new CellSyntaxError(err instanceof Error ? err.message : String(err));
129
131
  }
130
132
  }
131
- /** Rewrites module-only syntax in plain JavaScript into script syntax. */
133
+ /**
134
+ * Rewrites module-only syntax in plain JavaScript into script syntax.
135
+ *
136
+ * Top-level `const` becomes `let`, as do import bindings (#444). Under
137
+ * replMode a later evaluation may redeclare a top-level name only with the
138
+ * same kind of declaration: `const a` then `let a`, or `const a` then
139
+ * `import { a }`, fails with "already been declared" until a restart. With
140
+ * one kind, cells can be re-run and edited freely; the cost is that
141
+ * assigning to a top-level `const` from another cell doesn't throw.
142
+ */
132
143
  export function rewriteModuleSyntax(js) {
133
144
  const program = parseCell(js);
134
145
  const edits = [];
146
+ for (const statement of program.body) {
147
+ const decl = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
148
+ if (decl?.type === "VariableDeclaration" && decl.kind === "const") {
149
+ // Padded to `const`'s length, so columns stay where the debugger expects them.
150
+ edits.push({ start: decl.start, end: decl.start + "const".length, text: "let " });
151
+ }
152
+ }
135
153
  let importIndex = 0;
136
154
  full(program, (node) => {
137
155
  switch (node.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tlockney/cdp-kernel",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
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>",
@@ -1,30 +1,116 @@
1
1
  /** A comm message as a handler receives it. */
2
2
  export interface CommMessage {
3
+ /** The message's JSON data. */
3
4
  data: { [key: string]: unknown };
5
+ /** The message's metadata. */
4
6
  metadata: { [key: string]: unknown };
7
+ /** Binary buffers sent with the message. */
5
8
  buffers: Uint8Array[];
6
9
  }
7
10
 
11
+ /** Options for sending, opening or closing a comm. */
8
12
  export interface CommSendOptions {
13
+ /** Metadata for the message. Default `{}`. */
9
14
  metadata?: { [key: string]: unknown };
15
+ /** Binary buffers sent with the message. Default `[]`. */
10
16
  buffers?: Uint8Array[];
11
17
  }
12
18
 
13
- /** One end of a Jupyter comm channel. */
19
+ /** One end of a Jupyter comm channel, from `jupyter.comm.open` or a target handler. */
14
20
  export interface Comm {
21
+ /** The comm id, shared with the frontend's end. */
15
22
  readonly id: string;
23
+ /** The target the comm was opened on. */
16
24
  readonly targetName: string;
25
+ /** Whether either end has closed the comm. */
17
26
  readonly closed: boolean;
27
+ /**
28
+ * Send a message to the frontend's end. Does nothing once closed.
29
+ *
30
+ * @param data The message's JSON data. Default `{}`.
31
+ * @param options Metadata and binary buffers.
32
+ * @example
33
+ * ```ts
34
+ * const comm = jupyter.comm.open("my.target");
35
+ * comm.send({ ping: true }, { buffers: [new Uint8Array([1, 2, 3])] });
36
+ * ```
37
+ */
18
38
  send(data?: { [key: string]: unknown }, options?: CommSendOptions): void;
39
+ /**
40
+ * Close the comm and tell the frontend. Its `onClose` handlers start
41
+ * right away, without being awaited. Does nothing once closed.
42
+ *
43
+ * @param data Data sent with the close message. Default `{}`.
44
+ * @param options Metadata and binary buffers.
45
+ * @example
46
+ * ```ts
47
+ * const comm = jupyter.comm.open("my.target");
48
+ * comm.close({ reason: "done" });
49
+ * ```
50
+ */
19
51
  close(data?: { [key: string]: unknown }, options?: CommSendOptions): void;
52
+ /**
53
+ * Handle messages from the frontend's end. A handler runs like a cell:
54
+ * its output goes to the notebook, and an exception shows on stderr.
55
+ *
56
+ * @param handler Called with each message; may be async.
57
+ * @example
58
+ * ```ts
59
+ * const comm = jupyter.comm.open("my.target");
60
+ * comm.onMsg(({ data, buffers }) => console.log(data, buffers.length));
61
+ * ```
62
+ */
20
63
  onMsg(handler: (msg: CommMessage) => void | Promise<void>): void;
64
+ /**
65
+ * Handle the comm closing, from either end.
66
+ *
67
+ * @param handler Called with the close message; may be async.
68
+ * @example
69
+ * ```ts
70
+ * const comm = jupyter.comm.open("my.target");
71
+ * comm.onClose(() => console.log("closed"));
72
+ * ```
73
+ */
21
74
  onClose(handler: (msg: CommMessage) => void | Promise<void>): void;
22
75
  }
23
76
 
24
77
  /** `jupyter.comm`: open comms, and accept comms the frontend opens. */
25
78
  export interface CommApi {
79
+ /**
80
+ * Open a comm to a target the frontend registered.
81
+ *
82
+ * @param targetName The frontend's target name.
83
+ * @param data Data sent with the open message. Default `{}`.
84
+ * @param options Metadata and binary buffers.
85
+ * @returns The kernel's end of the comm.
86
+ * @example
87
+ * ```ts
88
+ * const comm = jupyter.comm.open("my.target", { hello: 1 });
89
+ * comm.onMsg((m) => console.log(m.data));
90
+ * ```
91
+ */
26
92
  open(targetName: string, data?: { [key: string]: unknown }, options?: CommSendOptions): Comm;
93
+ /**
94
+ * Accept comms the frontend opens on `targetName`. Registering the same
95
+ * name again replaces the handler.
96
+ *
97
+ * @param targetName The target name the frontend opens comms on.
98
+ * @param handler Called with the new comm and its open message.
99
+ * @example
100
+ * ```ts
101
+ * jupyter.comm.registerTarget("echo", (comm) => comm.onMsg((m) => comm.send(m.data)));
102
+ * ```
103
+ */
27
104
  registerTarget(targetName: string, handler: (comm: Comm, msg: CommMessage) => void | Promise<void>): void;
105
+ /**
106
+ * Stop accepting new comms on `targetName`. Comms already open stay open.
107
+ *
108
+ * @param targetName The target to remove.
109
+ * @example
110
+ * ```ts
111
+ * jupyter.comm.unregisterTarget("echo");
112
+ * ```
113
+ */
28
114
  unregisterTarget(targetName: string): void;
29
115
  }
30
116
 
@@ -3,8 +3,18 @@ import type { Widget, WidgetOptions } from "./widget.d.mts";
3
3
 
4
4
  export type { CommApi, Widget, WidgetOptions };
5
5
 
6
+ /**
7
+ * The symbol a class implements to choose how its instances appear. At
8
+ * runtime it is `Symbol.for("Jupyter.display")`, so a library can use that
9
+ * form without the `jupyter` global.
10
+ */
6
11
  export const $display: unique symbol;
7
12
 
13
+ /**
14
+ * A Jupyter MIME bundle: media type to content, such as
15
+ * `{ "text/plain": "42", "text/html": "<b>42</b>" }`. Binary images are
16
+ * base64 strings; JSON types (`application/json`, Vega-Lite) are objects.
17
+ */
8
18
  export type MediaBundle = { [mediaType: string]: unknown };
9
19
 
10
20
  export interface DisplayHost {
@@ -15,37 +25,262 @@ export interface DisplayHost {
15
25
  input: (prompt: string, password: boolean) => string | null;
16
26
  }
17
27
 
28
+ /** Options for `jupyter.display`. */
18
29
  export interface DisplayOptions {
30
+ /**
31
+ * Send a plain object as the MIME bundle itself instead of formatting
32
+ * it. Default `false`.
33
+ */
19
34
  raw?: boolean;
35
+ /**
36
+ * Replace every earlier output that has the same `display_id`, including
37
+ * one shown by an earlier cell, instead of adding an output. Needs
38
+ * `display_id`. Default `false`.
39
+ */
20
40
  update?: boolean;
41
+ /** An id that a later display with `update: true` can replace. */
21
42
  display_id?: string;
22
43
  }
23
44
 
45
+ /** A value that renders as rich output: it has a `[jupyter.$display]` method. */
24
46
  export interface Displayable {
47
+ /** Returns the value's MIME bundle, or a promise of one. */
25
48
  [$display]: () => MediaBundle | Promise<MediaBundle>;
26
49
  }
27
50
 
51
+ /**
52
+ * The `jupyter` global: how values appear in the notebook. The same object
53
+ * as `Deno.jupyter` in `deno jupyter`.
54
+ */
28
55
  export interface JupyterNamespace {
56
+ /**
57
+ * Send a raw Jupyter message on the kernel's output channel (IOPub), as a
58
+ * reply to the running cell. The other helpers are built on it. It does
59
+ * not check the message: a wrong field is ignored or mishandled by the
60
+ * frontend.
61
+ *
62
+ * @param msgType An IOPub message type, such as `"clear_output"` or
63
+ * `"display_data"`.
64
+ * @param content The message content, as the messaging spec defines it
65
+ * for `msgType`.
66
+ * @param options `metadata` for the message, and binary `buffers`.
67
+ * @example
68
+ * ```ts
69
+ * // Count down in place: clear_output with wait avoids a flicker.
70
+ * for (const n of [3, 2, 1]) {
71
+ * jupyter.broadcast("clear_output", { wait: true });
72
+ * await jupyter.display(jupyter.md`Launching in **${n}**…`);
73
+ * await new Promise((resolve) => setTimeout(resolve, 1000));
74
+ * }
75
+ * ```
76
+ * @see jupyter-api.md#raw-messages-with-broadcast
77
+ */
29
78
  broadcast(
30
79
  msgType: string,
31
80
  content: object,
32
81
  options?: { metadata?: object; buffers?: Uint8Array[] },
33
82
  ): void;
83
+ /**
84
+ * Show a value now, in the middle of a cell, or replace one shown
85
+ * earlier. The value can be anything a cell could end with: a template
86
+ * from `md`, `html` or `svg`, an image, a widget, an object with a
87
+ * `[jupyter.$display]` method, or a plain value, shown as text.
88
+ *
89
+ * @param obj The value to show.
90
+ * @param options `display_id` and `update` to replace an earlier output;
91
+ * `raw` to send a plain object as the MIME bundle itself.
92
+ * @returns Resolves once the output is sent. Without `await` the output
93
+ * still appears, but later output in the cell can appear before it.
94
+ * @example
95
+ * ```ts
96
+ * const bar = (pct: number) => jupyter.html`<progress value="${pct}" max="100"></progress>`;
97
+ * await jupyter.display(bar(0), { display_id: "download" });
98
+ * for (let pct = 25; pct <= 100; pct += 25) {
99
+ * await new Promise((resolve) => setTimeout(resolve, 500));
100
+ * await jupyter.display(bar(pct), { display_id: "download", update: true });
101
+ * }
102
+ * ```
103
+ * @example
104
+ * ```ts
105
+ * // A MIME bundle sent as is: JupyterLab renders it as a JSON tree.
106
+ * const user = { name: "Ada", active: true };
107
+ * await jupyter.display(
108
+ * { "application/json": user, "text/plain": JSON.stringify(user) },
109
+ * { raw: true },
110
+ * );
111
+ * ```
112
+ * @see jupyter-api.md#showing-output-with-display
113
+ */
34
114
  display(obj: unknown, options?: DisplayOptions): Promise<void>;
115
+ /**
116
+ * The MIME bundle that displaying a value would send, without showing
117
+ * anything. For plain values `text/plain` holds terminal color codes,
118
+ * as the notebook shows them.
119
+ *
120
+ * @param obj The value to format.
121
+ * @returns The bundle, for example `{ "text/plain": "\x1b[33m42\x1b[39m" }`
122
+ * for `42`.
123
+ * @example
124
+ * ```ts
125
+ * const bundle = await jupyter.format(jupyter.md`# Weekly report`);
126
+ * bundle["text/markdown"]; // "# Weekly report"
127
+ * ```
128
+ * @see jupyter-api.md#getting-a-bundle-with-format
129
+ */
35
130
  format(obj: unknown): Promise<MediaBundle>;
131
+ /**
132
+ * A tagged template that renders as Markdown. Interpolated values are
133
+ * inserted as is, without escaping.
134
+ *
135
+ * @returns A value that shows as `text/markdown` as a cell's result or
136
+ * through `display`.
137
+ * @example
138
+ * ```ts
139
+ * const scores = { Ada: 92, Grace: 88 };
140
+ * const rows = Object.entries(scores).map(([n, s]) => `| ${n} | ${s} |`).join("\n");
141
+ * jupyter.md`| Name | Score |
142
+ * | --- | --- |
143
+ * ${rows}`;
144
+ * ```
145
+ * @see jupyter-api.md#markdown
146
+ */
36
147
  md(strings: TemplateStringsArray, ...values: unknown[]): Displayable;
148
+ /**
149
+ * A tagged template that renders as HTML. Interpolated values are
150
+ * inserted as is, without escaping: escape text from users, files or
151
+ * the network yourself.
152
+ *
153
+ * @returns A value that shows as `text/html`.
154
+ * @example
155
+ * ```ts
156
+ * const escape = (s: string) => s.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
157
+ * const comment = "<script>alert(1)</script>";
158
+ * jupyter.html`<p>${escape(comment)}</p>`;
159
+ * ```
160
+ * @see jupyter-api.md#html
161
+ */
37
162
  html(strings: TemplateStringsArray, ...values: unknown[]): Displayable;
163
+ /**
164
+ * A tagged template that renders as SVG. Include the `xmlns` attribute.
165
+ * Interpolated values are inserted as is, without escaping.
166
+ *
167
+ * @returns A value that shows as `image/svg+xml`.
168
+ * @example
169
+ * ```ts
170
+ * const data = [4, 8, 15, 16];
171
+ * const bars = data.map((v, i) => `<rect x="${i * 20}" y="${40 - v * 2}" width="16" height="${v * 2}"/>`).join("");
172
+ * jupyter.svg`<svg xmlns="http://www.w3.org/2000/svg" width="80" height="40">${bars}</svg>`;
173
+ * ```
174
+ * @see jupyter-api.md#svg
175
+ */
38
176
  svg(strings: TemplateStringsArray, ...values: unknown[]): Displayable;
177
+ /**
178
+ * Show a PNG or JPEG. The format is read from the bytes, not the file
179
+ * name.
180
+ *
181
+ * @param obj A file path, relative to the notebook's directory, or the
182
+ * image's bytes.
183
+ * @returns A value that shows as `image/png` or `image/jpeg`.
184
+ * @throws {TypeError} The bytes are not PNG or JPEG, or the path can't
185
+ * be read.
186
+ * @availability Paths need a file system: on workerd and celld pass
187
+ * bytes.
188
+ * @example
189
+ * ```ts
190
+ * jupyter.image("./chart.png");
191
+ * ```
192
+ * @example
193
+ * ```ts
194
+ * const response = await fetch("https://www.python.org/static/img/python-logo.png");
195
+ * jupyter.image(new Uint8Array(await response.arrayBuffer()));
196
+ * ```
197
+ * @see jupyter-api.md#images
198
+ */
39
199
  image(obj: string | Uint8Array): Displayable;
200
+ /**
201
+ * The symbol a class implements to choose how its instances appear. The
202
+ * method returns a MIME bundle, or a promise of one. It is used for a
203
+ * cell's result, `display` and `format`, but not for objects nested in
204
+ * other values. At runtime it is `Symbol.for("Jupyter.display")`.
205
+ *
206
+ * @example
207
+ * ```ts
208
+ * class Temperature {
209
+ * celsius: number;
210
+ * constructor(celsius: number) {
211
+ * this.celsius = celsius;
212
+ * }
213
+ * [jupyter.$display]() {
214
+ * return { "text/plain": `${this.celsius}°C`, "text/html": `<b>${this.celsius}°C</b>` };
215
+ * }
216
+ * }
217
+ * new Temperature(21);
218
+ * ```
219
+ * @see jupyter-api.md#custom-display-for-your-own-classes
220
+ */
40
221
  $display: typeof $display;
41
- /** Jupyter comms: bidirectional channels to the frontend. */
222
+ /**
223
+ * Jupyter comms: message channels to the frontend outside cell
224
+ * execution. Its methods are in the widgets and comms reference.
225
+ *
226
+ * @see widgets.md#comms
227
+ */
42
228
  comm: CommApi;
43
- /** An anywidget-compatible widget; display the result to show it. */
229
+ /**
230
+ * Create an anywidget-compatible widget model. Display it (or end the
231
+ * cell with it) to show a view.
232
+ *
233
+ * @param options The view module, its CSS, and the initial state.
234
+ * @returns The model, synced with every view of it.
235
+ * @throws {TypeError} `options.esm` is not a string.
236
+ * @availability The frontend needs the anywidget extension.
237
+ * @example
238
+ * ```ts
239
+ * const counter = jupyter.widget({
240
+ * esm: `export default {
241
+ * render({ model, el }) {
242
+ * const button = document.createElement("button");
243
+ * const update = () => {
244
+ * button.textContent = "count is " + model.get("count");
245
+ * };
246
+ * button.onclick = () => {
247
+ * model.set("count", model.get("count") + 1);
248
+ * model.save_changes();
249
+ * };
250
+ * model.on("change:count", update);
251
+ * update();
252
+ * el.append(button);
253
+ * },
254
+ * };`,
255
+ * state: { count: 0 },
256
+ * });
257
+ * counter; // the cell's result: displays the widget
258
+ * ```
259
+ * ![A JupyterLab cell that creates a counter widget with jupyter.widget. Below the code, the output is a button reading "count is 3" after three clicks.](https://cdp-kernel.org/images/widgets/counter.png)
260
+ * @see widgets.md#a-first-widget-a-counter
261
+ */
44
262
  widget(options: WidgetOptions): Widget;
45
263
  /**
46
- * Aborted when the kernel interrupts this cell. Each cell gets its own,
47
- * as does a comm handler that starts while no cell runs. Read it when
48
- * the work starts: a later read gets the signal of whatever started last.
264
+ * An `AbortSignal` that an interrupt aborts, so code waiting on something
265
+ * only it can end can stop and clean up. Each cell gets its own, as does
266
+ * a comm handler that starts while no cell runs. Read it once when the
267
+ * work starts: a later read gets the signal of whatever started last.
268
+ * The abort reason is a `DOMException` named `AbortError`.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * const response = await fetch("https://example.com/large.csv", { signal: jupyter.signal });
273
+ * ```
274
+ * @example
275
+ * ```ts
276
+ * const signal = jupyter.signal;
277
+ * const ask = jupyter.comm.open("my.app", { question: "ready?" });
278
+ * const answer = await new Promise((resolve, reject) => {
279
+ * ask.onMsg((m) => resolve(m.data));
280
+ * signal.addEventListener("abort", () => { ask.close(); reject(signal.reason); }, { once: true });
281
+ * });
282
+ * ```
283
+ * @see jupyter-api.md#stopping-on-interrupt-jupyter-signal
49
284
  */
50
285
  readonly signal: AbortSignal;
51
286
  }