@tlockney/cdp-kernel 0.6.0 → 0.7.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 CHANGED
@@ -55,7 +55,7 @@ deno run -A jsr:@tlockney/cdp-kernel/cli install --runtime deno
55
55
  Each install writes a Jupyter kernelspec (`cdp-node`, `cdp-deno`, …). Pick
56
56
  it in JupyterLab or VS Code. To run notebooks with no Python installed,
57
57
  serve JupyterLab with
58
- [Ganymede](https://ganymede-server.com/docs/workstation). Linux and macOS
58
+ [Ganymede](https://ganymede-server.com/docs/install). Linux and macOS
59
59
  are supported; Windows isn't yet. See
60
60
  [Install](https://cdp-kernel.org/docs/install) for `--deno-compat`, release-day installs on Deno, target runtime
61
61
  requirements, and installing from a checkout.
@@ -0,0 +1,27 @@
1
+ import type { ChannelName, KernelChannels, Received } from "../kernel-core.ts";
2
+ import type { JsonObject, OutgoingMessage } from "../message.ts";
3
+ /** Every channel a message travels on, iopub included. */
4
+ export type Channel = ChannelName | "iopub";
5
+ /** A Jupyter message as JSON, the shape a frontend sends and receives. */
6
+ export interface JupyterMessage {
7
+ header: JsonObject;
8
+ parent_header: JsonObject;
9
+ metadata: JsonObject;
10
+ content: JsonObject;
11
+ buffers?: (ArrayBuffer | ArrayBufferView)[];
12
+ channel?: string;
13
+ }
14
+ export type MessageListener = (channel: Channel, msg: JupyterMessage) => void;
15
+ export declare class PageChannels implements KernelChannels {
16
+ #private;
17
+ readonly ready: Promise<unknown>;
18
+ /** A message from the frontend. One without a msg_id and msg_type is dropped. */
19
+ deliver(channel: ChannelName, msg: JupyterMessage): void;
20
+ onMessage(listener: MessageListener): () => void;
21
+ recv(channel: ChannelName): Promise<Received | null>;
22
+ shellQueued(): number;
23
+ reply(channel: ChannelName, _peer: string, msg: OutgoingMessage): void;
24
+ publish(msg: OutgoingMessage): void;
25
+ stdinPeers(): number;
26
+ sendStdin(msg: OutgoingMessage): void;
27
+ }
@@ -0,0 +1,90 @@
1
+ // KernelChannels inside a page: the frontend hands the kernel messages in
2
+ // Jupyter's JSON shape (as the websocket's JSON protocol and
3
+ // @jupyterlab/services carry them) and gets messages back the same way.
4
+ // There is one frontend, so one peer, and no signing.
5
+ const PEER = "page";
6
+ class Queue {
7
+ #items = [];
8
+ #waiters = [];
9
+ get size() {
10
+ return this.#items.length;
11
+ }
12
+ push(item) {
13
+ const waiter = this.#waiters.shift();
14
+ if (waiter)
15
+ waiter(item);
16
+ else
17
+ this.#items.push(item);
18
+ }
19
+ pop() {
20
+ const item = this.#items.shift();
21
+ if (item !== undefined)
22
+ return Promise.resolve(item);
23
+ return new Promise((resolve) => this.#waiters.push(resolve));
24
+ }
25
+ }
26
+ function bytes(buffer) {
27
+ return buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
28
+ }
29
+ function isHeader(value) {
30
+ return typeof value.msg_id === "string" && typeof value.msg_type === "string";
31
+ }
32
+ function isObject(value) {
33
+ return typeof value === "object" && value !== null && !Array.isArray(value);
34
+ }
35
+ export class PageChannels {
36
+ ready = Promise.resolve();
37
+ #queues = { shell: new Queue(), control: new Queue(), stdin: new Queue() };
38
+ #listeners = new Set();
39
+ /** A message from the frontend. One without a msg_id and msg_type is dropped. */
40
+ deliver(channel, msg) {
41
+ const { header, parent_header: parentHeader, metadata, content } = msg;
42
+ const valid = isObject(header) && isHeader(header) && isObject(content);
43
+ this.#queues[channel].push(valid
44
+ ? {
45
+ peer: PEER,
46
+ msg: {
47
+ header,
48
+ parentHeader: isObject(parentHeader) ? parentHeader : {},
49
+ metadata: isObject(metadata) ? metadata : {},
50
+ content,
51
+ buffers: (msg.buffers ?? []).map(bytes),
52
+ },
53
+ }
54
+ : null);
55
+ }
56
+ onMessage(listener) {
57
+ this.#listeners.add(listener);
58
+ return () => this.#listeners.delete(listener);
59
+ }
60
+ #emit(channel, msg) {
61
+ const out = {
62
+ channel,
63
+ header: msg.header,
64
+ parent_header: msg.parentHeader,
65
+ metadata: msg.metadata,
66
+ content: msg.content,
67
+ buffers: msg.buffers,
68
+ };
69
+ for (const listener of this.#listeners)
70
+ listener(channel, out);
71
+ }
72
+ recv(channel) {
73
+ return this.#queues[channel].pop();
74
+ }
75
+ shellQueued() {
76
+ return this.#queues.shell.size;
77
+ }
78
+ reply(channel, _peer, msg) {
79
+ this.#emit(channel, msg);
80
+ }
81
+ publish(msg) {
82
+ this.#emit("iopub", msg);
83
+ }
84
+ stdinPeers() {
85
+ return 1;
86
+ }
87
+ sendStdin(msg) {
88
+ this.#emit("stdin", msg);
89
+ }
90
+ }