@tlockney/cdp-kernel 0.5.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 +27 -6
- package/dist/browser/channels.d.ts +27 -0
- package/dist/browser/channels.js +90 -0
- package/dist/browser/kernel.js +29674 -0
- package/dist/browser/kernel.js.map +7 -0
- package/dist/browser/mod.d.ts +20 -0
- package/dist/browser/mod.js +38 -0
- package/dist/browser/target.d.ts +71 -0
- package/dist/browser/target.js +206 -0
- package/dist/browser/worker.js +1000 -0
- package/dist/browser/worker.js.map +7 -0
- package/dist/cdp.d.ts +11 -0
- package/dist/cdp.js +6 -1
- package/dist/celld-target.js +9 -12
- package/dist/children.d.ts +25 -3
- package/dist/children.js +84 -2
- package/dist/debugger.d.ts +2 -0
- package/dist/debugger.js +12 -6
- package/dist/deno-types.d.ts +30 -0
- package/dist/deno-types.js +93 -0
- package/dist/kernel-core.d.ts +69 -0
- package/dist/kernel-core.js +754 -0
- package/dist/kernel.d.ts +12 -1
- package/dist/kernel.js +132 -605
- 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/message.d.ts +34 -0
- package/dist/message.js +14 -0
- package/dist/mod.d.ts +1 -0
- package/dist/mod.js +1 -0
- 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 +2 -2
- 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-api.d.ts +80 -0
- package/dist/target-api.js +22 -0
- package/dist/target.d.ts +26 -74
- package/dist/target.js +232 -58
- package/dist/traceback.d.ts +75 -0
- package/dist/traceback.js +200 -0
- package/dist/transform.d.ts +22 -1
- package/dist/transform.js +46 -43
- package/dist/wire.d.ts +4 -24
- package/dist/wire.js +3 -12
- package/dist/workerd-target.js +1 -1
- package/dist/workers-project.js +13 -2
- package/dist/zmq-channels.d.ts +14 -0
- package/dist/zmq-channels.js +59 -0
- package/dist/zmtp.d.ts +2 -0
- package/dist/zmtp.js +12 -0
- package/package.json +15 -5
- package/runtime/bootstrap.mjs +113 -33
- package/runtime/browser-worker.mjs +119 -0
- package/runtime/cdn.d.mts +2 -0
- package/runtime/cdn.mjs +17 -0
- package/runtime/display.mjs +202 -1
- package/runtime/format.d.mts +2 -0
- package/runtime/format.mjs +125 -0
- package/runtime/introspect.mjs +1 -1
- package/runtime/node-hooks.d.mts +6 -0
- package/runtime/node-hooks.mjs +11 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// The worker thread that runs TypeScript's language service for the kernel
|
|
2
|
+
// (#273). It owns a NotebookTypes (src/notebook-types.ts) and answers the
|
|
3
|
+
// host's requests (src/language-service.ts) in order. Loading TypeScript
|
|
4
|
+
// and the first program take most of a second, which here doesn't hold up
|
|
5
|
+
// heartbeats, shell or control messages.
|
|
6
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import ts from "typescript";
|
|
10
|
+
import { ensureInstalledAsync } from "../runtime/node-hooks.mjs";
|
|
11
|
+
import { kernelGlobals, NotebookTypes } from "./notebook-types.js";
|
|
12
|
+
import { DenoResolver, denoTypes } from "./deno-types.js";
|
|
13
|
+
const options = workerData;
|
|
14
|
+
const types = new NotebookTypes(ts, {
|
|
15
|
+
notebookDir: options.notebookDir,
|
|
16
|
+
npmCache: options.npmCache,
|
|
17
|
+
deno: options.deno === null
|
|
18
|
+
? undefined
|
|
19
|
+
: new DenoResolver({ config: options.deno.config, root: join(options.notebookDir, "__cdpk_remote__") }),
|
|
20
|
+
types: [],
|
|
21
|
+
typeFiles: options.typeFiles,
|
|
22
|
+
globals: kernelGlobals(options.display),
|
|
23
|
+
});
|
|
24
|
+
if (options.denoTypes) {
|
|
25
|
+
const declarations = denoTypes();
|
|
26
|
+
if (declarations !== null)
|
|
27
|
+
types.addVirtualTypeFile("__cdpk_deno__.d.ts", declarations);
|
|
28
|
+
else
|
|
29
|
+
process.stderr.write("[cdp-kernel] no Deno types for completion: `deno types` failed\n");
|
|
30
|
+
}
|
|
31
|
+
/** A package's declaration entry point: package.json "types"/"typings", else index.d.ts. */
|
|
32
|
+
function typesEntry(dir) {
|
|
33
|
+
try {
|
|
34
|
+
const manifest = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
35
|
+
const entry = manifest.types ?? manifest.typings;
|
|
36
|
+
if (typeof entry === "string")
|
|
37
|
+
return join(dir, entry);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// No readable package.json: fall back to the convention.
|
|
41
|
+
}
|
|
42
|
+
return join(dir, "index.d.ts");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A type package for the target's globals (@types/node on Node, @types/bun,
|
|
46
|
+
* @cloudflare/workers-types): the
|
|
47
|
+
* notebook project's own when it has one, else installed once into the
|
|
48
|
+
* kernel's npm cache, in the background. Completion goes on without it
|
|
49
|
+
* meanwhile, and without it at all if the install fails (offline).
|
|
50
|
+
*/
|
|
51
|
+
async function addTypePackage({ name, range }) {
|
|
52
|
+
const short = name.replace(/^@types\//, "");
|
|
53
|
+
if (types.resolvesTypes(short)) {
|
|
54
|
+
types.addTypeNames([short]);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const { dir, alias } = await ensureInstalledAsync({ name, range });
|
|
59
|
+
types.addTypeFiles([typesEntry(join(dir, "node_modules", alias))]);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
process.stderr.write(`[cdp-kernel] no ${name} for completion: ${err instanceof Error ? err.message : err}\n`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const pkg of options.typePackages)
|
|
66
|
+
void addTypePackage(pkg);
|
|
67
|
+
parentPort?.on("message", (request) => {
|
|
68
|
+
let response;
|
|
69
|
+
switch (request.type) {
|
|
70
|
+
case "cell":
|
|
71
|
+
types.addCell(request.code);
|
|
72
|
+
return;
|
|
73
|
+
case "reset":
|
|
74
|
+
types.reset();
|
|
75
|
+
return;
|
|
76
|
+
case "complete":
|
|
77
|
+
case "quickInfo":
|
|
78
|
+
try {
|
|
79
|
+
response = request.type === "complete"
|
|
80
|
+
? { id: request.id, result: types.complete(request.code, request.cursor) }
|
|
81
|
+
: { id: request.id, result: types.quickInfo(request.code, request.cursor) };
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
response = { id: request.id, error: err instanceof Error ? err.stack ?? err.message : String(err) };
|
|
85
|
+
}
|
|
86
|
+
parentPort?.postMessage(response);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { CompletionResult } from "./notebook-types.ts";
|
|
2
|
+
export type { CompletionEntry, CompletionResult } from "./notebook-types.ts";
|
|
3
|
+
/** What the worker is started with. */
|
|
4
|
+
export interface WorkerOptions {
|
|
5
|
+
notebookDir: string;
|
|
6
|
+
/** See NotebookTypesOptions.npmCache. */
|
|
7
|
+
npmCache: "npm" | "bun" | null;
|
|
8
|
+
/** A Deno target: resolve imports with `deno info`, under this project config if any. */
|
|
9
|
+
deno: {
|
|
10
|
+
config: string | null;
|
|
11
|
+
} | null;
|
|
12
|
+
/** Add `deno types` (the Deno namespace) to the globals. */
|
|
13
|
+
denoTypes: boolean;
|
|
14
|
+
/** Declaration files for the target's globals, e.g. a Workers project's worker-configuration.d.ts. */
|
|
15
|
+
typeFiles: string[];
|
|
16
|
+
/** runtime/display.mjs, which types `jupyter`; null if not a local file. */
|
|
17
|
+
display: string | null;
|
|
18
|
+
/** Type packages for the target's globals, e.g. @types/node. */
|
|
19
|
+
typePackages: {
|
|
20
|
+
name: string;
|
|
21
|
+
range: string;
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
export type WorkerRequest = {
|
|
25
|
+
type: "cell";
|
|
26
|
+
code: string;
|
|
27
|
+
} | {
|
|
28
|
+
type: "reset";
|
|
29
|
+
} | {
|
|
30
|
+
type: "complete" | "quickInfo";
|
|
31
|
+
id: number;
|
|
32
|
+
code: string;
|
|
33
|
+
cursor: number;
|
|
34
|
+
};
|
|
35
|
+
export type WorkerResponse = {
|
|
36
|
+
id: number;
|
|
37
|
+
result: CompletionResult | string | null;
|
|
38
|
+
} | {
|
|
39
|
+
id: number;
|
|
40
|
+
error: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* What to start the worker from. Deno's node:worker_threads takes only
|
|
44
|
+
* file: and data: URLs, so a kernel run from JSR (https:) starts a data:
|
|
45
|
+
* module that imports the worker.
|
|
46
|
+
*/
|
|
47
|
+
export declare function workerEntry(url?: URL): URL;
|
|
48
|
+
export declare class LanguageService {
|
|
49
|
+
#private;
|
|
50
|
+
constructor(options: Omit<WorkerOptions, "display">, { budgetMs, log }?: {
|
|
51
|
+
budgetMs?: number;
|
|
52
|
+
log?: (text: string) => void;
|
|
53
|
+
});
|
|
54
|
+
/** A cell that ran (even if it threw): its declarations exist now. */
|
|
55
|
+
addCell(code: string): void;
|
|
56
|
+
/** The target restarted, so what the cells declared is gone. */
|
|
57
|
+
reset(): void;
|
|
58
|
+
/** Typed completions, or null if the service isn't ready within the budget. */
|
|
59
|
+
complete(code: string, cursor: number): Promise<CompletionResult | null>;
|
|
60
|
+
/** Type, signature and JSDoc for the name at the cursor, or null. */
|
|
61
|
+
quickInfo(code: string, cursor: number): Promise<string | null>;
|
|
62
|
+
/** Ends the worker; the next request starts another. */
|
|
63
|
+
close(): void;
|
|
64
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// The kernel's side of typed completion and inspection (#273, #274): starts the language-service
|
|
2
|
+
// worker (src/language-service-worker.ts) on the first request, keeps it fed
|
|
3
|
+
// with executed cells, and gives each request a time budget, after which
|
|
4
|
+
// the kernel answers from the runtime alone. The worker is ended after a
|
|
5
|
+
// while without requests, which gives its memory back (about 150 MB).
|
|
6
|
+
import { Worker } from "node:worker_threads";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
/**
|
|
9
|
+
* How long a request waits for the worker. JupyterLab gives a kernel's
|
|
10
|
+
* completions a second, and the runtime's half runs alongside.
|
|
11
|
+
*/
|
|
12
|
+
const BUDGET_MS = 500;
|
|
13
|
+
const IDLE_MS = 10 * 60 * 1000;
|
|
14
|
+
// Compiled to dist/*.js for the npm package, run as .ts from a checkout.
|
|
15
|
+
const WORKER_URL = new URL(import.meta.url.endsWith(".ts") ? "./language-service-worker.ts" : "./language-service-worker.js", import.meta.url);
|
|
16
|
+
const DISPLAY_URL = new URL("../runtime/display.mjs", import.meta.url);
|
|
17
|
+
/**
|
|
18
|
+
* What to start the worker from. Deno's node:worker_threads takes only
|
|
19
|
+
* file: and data: URLs, so a kernel run from JSR (https:) starts a data:
|
|
20
|
+
* module that imports the worker.
|
|
21
|
+
*/
|
|
22
|
+
export function workerEntry(url = WORKER_URL) {
|
|
23
|
+
if (url.protocol === "file:")
|
|
24
|
+
return url;
|
|
25
|
+
return new URL(`data:text/javascript,${encodeURIComponent(`import ${JSON.stringify(url.href)};`)}`);
|
|
26
|
+
}
|
|
27
|
+
export class LanguageService {
|
|
28
|
+
#options;
|
|
29
|
+
#budgetMs;
|
|
30
|
+
#log;
|
|
31
|
+
/** Executed cells, replayed to a new worker. */
|
|
32
|
+
#cells = [];
|
|
33
|
+
#pending = new Map();
|
|
34
|
+
#worker = null;
|
|
35
|
+
#nextId = 1;
|
|
36
|
+
#idle = null;
|
|
37
|
+
/** The worker couldn't start: don't keep trying. */
|
|
38
|
+
#broken = false;
|
|
39
|
+
constructor(options, { budgetMs = BUDGET_MS, log = () => { } } = {}) {
|
|
40
|
+
this.#options = { ...options, display: DISPLAY_URL.protocol === "file:" ? fileURLToPath(DISPLAY_URL) : null };
|
|
41
|
+
this.#budgetMs = budgetMs;
|
|
42
|
+
this.#log = log;
|
|
43
|
+
}
|
|
44
|
+
/** A cell that ran (even if it threw): its declarations exist now. */
|
|
45
|
+
addCell(code) {
|
|
46
|
+
this.#cells.push(code);
|
|
47
|
+
this.#worker?.postMessage({ type: "cell", code });
|
|
48
|
+
}
|
|
49
|
+
/** The target restarted, so what the cells declared is gone. */
|
|
50
|
+
reset() {
|
|
51
|
+
this.#cells.length = 0;
|
|
52
|
+
this.#worker?.postMessage({ type: "reset" });
|
|
53
|
+
}
|
|
54
|
+
/** Typed completions, or null if the service isn't ready within the budget. */
|
|
55
|
+
async complete(code, cursor) {
|
|
56
|
+
return (await this.#request("complete", code, cursor));
|
|
57
|
+
}
|
|
58
|
+
/** Type, signature and JSDoc for the name at the cursor, or null. */
|
|
59
|
+
async quickInfo(code, cursor) {
|
|
60
|
+
return (await this.#request("quickInfo", code, cursor));
|
|
61
|
+
}
|
|
62
|
+
#request(type, code, cursor) {
|
|
63
|
+
const worker = this.#ensureWorker();
|
|
64
|
+
if (worker === null)
|
|
65
|
+
return Promise.resolve(null);
|
|
66
|
+
const id = this.#nextId++;
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
const timer = setTimeout(() => settle(null), this.#budgetMs);
|
|
69
|
+
const settle = (response) => {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
this.#pending.delete(id);
|
|
72
|
+
if (response !== null && "error" in response)
|
|
73
|
+
this.#log(`${type} failed: ${response.error}`);
|
|
74
|
+
resolve(response !== null && "result" in response ? response.result : null);
|
|
75
|
+
};
|
|
76
|
+
this.#pending.set(id, settle);
|
|
77
|
+
worker.postMessage({ type, id, code, cursor });
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** Ends the worker; the next request starts another. */
|
|
81
|
+
close() {
|
|
82
|
+
if (this.#idle !== null)
|
|
83
|
+
clearTimeout(this.#idle);
|
|
84
|
+
this.#idle = null;
|
|
85
|
+
const worker = this.#worker;
|
|
86
|
+
this.#worker = null;
|
|
87
|
+
for (const settle of this.#pending.values())
|
|
88
|
+
settle(null);
|
|
89
|
+
void worker?.terminate();
|
|
90
|
+
}
|
|
91
|
+
#ensureWorker() {
|
|
92
|
+
if (this.#broken)
|
|
93
|
+
return null;
|
|
94
|
+
if (this.#idle !== null)
|
|
95
|
+
clearTimeout(this.#idle);
|
|
96
|
+
this.#idle = setTimeout(() => this.close(), IDLE_MS);
|
|
97
|
+
this.#idle.unref();
|
|
98
|
+
if (this.#worker !== null)
|
|
99
|
+
return this.#worker;
|
|
100
|
+
let worker;
|
|
101
|
+
try {
|
|
102
|
+
worker = new Worker(workerEntry(), { workerData: this.#options });
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
this.#broken = true;
|
|
106
|
+
this.#log(`typed completion unavailable: ${err instanceof Error ? err.message : err}`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
// An idle worker mustn't keep the kernel alive.
|
|
110
|
+
worker.unref();
|
|
111
|
+
worker.on("message", (response) => this.#pending.get(response.id)?.(response));
|
|
112
|
+
worker.on("error", (err) => {
|
|
113
|
+
// E.g. typescript failed to load. Completion goes on from the runtime.
|
|
114
|
+
this.#broken = true;
|
|
115
|
+
this.#log(`typed completion unavailable: ${err instanceof Error ? err.message : err}`);
|
|
116
|
+
if (this.#worker === worker)
|
|
117
|
+
this.close();
|
|
118
|
+
});
|
|
119
|
+
worker.on("exit", () => {
|
|
120
|
+
if (this.#worker === worker)
|
|
121
|
+
this.close();
|
|
122
|
+
});
|
|
123
|
+
for (const code of this.#cells)
|
|
124
|
+
worker.postMessage({ type: "cell", code });
|
|
125
|
+
this.#worker = worker;
|
|
126
|
+
return worker;
|
|
127
|
+
}
|
|
128
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// cdp-kernel CLI.
|
|
3
|
-
// cdp-kernel install [--runtime node|deno|bun|workerd|celld] [--deno-compat] [--name NAME] [--display NAME]
|
|
4
|
-
// cdp-kernel kernel --runtime node|deno|bun|workerd|celld [--deno-compat] --conn CONNECTION_FILE
|
|
3
|
+
// cdp-kernel install [--runtime node|deno|bun|workerd|celld] [--deno-compat] [--type-strip esbuild|sucrase] [--no-language-service] [--name NAME] [--display NAME]
|
|
4
|
+
// cdp-kernel kernel --runtime node|deno|bun|workerd|celld [--deno-compat] [--type-strip esbuild|sucrase] [--no-language-service] --conn CONNECTION_FILE
|
|
5
|
+
// cdp-kernel list
|
|
6
|
+
// cdp-kernel uninstall [NAME...] [--name NAME] [--all]
|
|
5
7
|
// cdp-kernel version
|
|
6
8
|
//
|
|
7
9
|
// --deno-compat (Node only) adds a `Deno` namespace for notebooks written for
|
|
8
|
-
// `deno jupyter`; see runtime/deno-compat.mjs.
|
|
10
|
+
// `deno jupyter`; see runtime/deno-compat.mjs. --type-strip picks what
|
|
11
|
+
// removes cells' types (src/transform.ts). --no-language-service turns off
|
|
12
|
+
// typed completion (src/language-service.ts), which costs about 150 MB
|
|
13
|
+
// while in use.
|
|
9
14
|
import process from "node:process";
|
|
10
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11
16
|
import { homedir, platform } from "node:os";
|
|
12
17
|
import { join } from "node:path";
|
|
13
18
|
import { parseArgs } from "node:util";
|
|
14
19
|
import { Kernel, parseConnectionInfo } from "./kernel.js";
|
|
20
|
+
import { isTypeStripperName } from "./transform.js";
|
|
15
21
|
import { hostRuntime, kernelArgv, moduleOrigin, VERSION } from "./package.js";
|
|
16
22
|
function isRuntime(value) {
|
|
17
23
|
return value === "node" || value === "deno" || value === "bun" || value === "workerd" || value === "celld";
|
|
@@ -25,7 +31,7 @@ function kernelsDir() {
|
|
|
25
31
|
return join(process.env.APPDATA ?? homedir(), "jupyter", "kernels");
|
|
26
32
|
return join(process.env.XDG_DATA_HOME ?? join(homedir(), ".local", "share"), "jupyter", "kernels");
|
|
27
33
|
}
|
|
28
|
-
function install(runtime, denoCompat, name, display) {
|
|
34
|
+
function install(runtime, denoCompat, typeStrip, languageService, name, display) {
|
|
29
35
|
const dir = join(kernelsDir(), name);
|
|
30
36
|
mkdirSync(dir, { recursive: true });
|
|
31
37
|
const spec = {
|
|
@@ -34,6 +40,8 @@ function install(runtime, denoCompat, name, display) {
|
|
|
34
40
|
"--runtime",
|
|
35
41
|
runtime,
|
|
36
42
|
...(denoCompat ? ["--deno-compat"] : []),
|
|
43
|
+
...(typeStrip !== "esbuild" ? ["--type-strip", typeStrip] : []),
|
|
44
|
+
...(languageService ? [] : ["--no-language-service"]),
|
|
37
45
|
"--conn",
|
|
38
46
|
"{connection_file}",
|
|
39
47
|
]),
|
|
@@ -49,6 +57,77 @@ function install(runtime, denoCompat, name, display) {
|
|
|
49
57
|
writeFileSync(join(dir, "kernel.json"), JSON.stringify(spec, null, 2) + "\n");
|
|
50
58
|
console.log(`installed kernelspec ${name} -> ${dir}`);
|
|
51
59
|
}
|
|
60
|
+
// A kernelspec this package wrote: install() always records the runtime in
|
|
61
|
+
// metadata and passes the connection file to the kernel subcommand. Other
|
|
62
|
+
// kernels' specs in the same directory are left alone.
|
|
63
|
+
function readOwnSpec(name) {
|
|
64
|
+
const dir = join(kernelsDir(), name);
|
|
65
|
+
let spec;
|
|
66
|
+
try {
|
|
67
|
+
spec = JSON.parse(readFileSync(join(dir, "kernel.json"), "utf8"));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const argv = Array.isArray(spec.argv) ? spec.argv : [];
|
|
73
|
+
const runtime = spec.metadata?.runtime;
|
|
74
|
+
if (!isRuntime(runtime) || !argv.includes("kernel") || !argv.includes("{connection_file}"))
|
|
75
|
+
return undefined;
|
|
76
|
+
const version = typeof spec.metadata?.version === "string" ? spec.metadata.version : undefined;
|
|
77
|
+
return { name, dir, runtime, version };
|
|
78
|
+
}
|
|
79
|
+
function installedSpecs() {
|
|
80
|
+
const root = kernelsDir();
|
|
81
|
+
if (!existsSync(root))
|
|
82
|
+
return [];
|
|
83
|
+
return readdirSync(root, { withFileTypes: true })
|
|
84
|
+
.filter((entry) => entry.isDirectory())
|
|
85
|
+
.map((entry) => readOwnSpec(entry.name))
|
|
86
|
+
.filter((spec) => spec !== undefined)
|
|
87
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
88
|
+
}
|
|
89
|
+
function list() {
|
|
90
|
+
const specs = installedSpecs();
|
|
91
|
+
if (specs.length === 0) {
|
|
92
|
+
console.log(`no cdp-kernel kernelspecs in ${kernelsDir()}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const width = Math.max(...specs.map((s) => s.name.length));
|
|
96
|
+
for (const s of specs) {
|
|
97
|
+
console.log(`${s.name.padEnd(width)} ${s.runtime.padEnd(7)} ${(s.version ?? "-").padEnd(7)} ${s.dir}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Jupyter's kernel names; anything else could point outside the kernels
|
|
101
|
+
// directory.
|
|
102
|
+
const KERNEL_NAME = /^[A-Za-z0-9._-]+$/;
|
|
103
|
+
function uninstall(names, all) {
|
|
104
|
+
if (all)
|
|
105
|
+
names = installedSpecs().map((s) => s.name);
|
|
106
|
+
else if (names.length === 0) {
|
|
107
|
+
console.error("uninstall: name the kernelspecs to remove, or pass --all. Installed:");
|
|
108
|
+
list();
|
|
109
|
+
process.exit(2);
|
|
110
|
+
}
|
|
111
|
+
let failed = false;
|
|
112
|
+
for (const name of names) {
|
|
113
|
+
const valid = KERNEL_NAME.test(name) && name !== "." && name !== "..";
|
|
114
|
+
const spec = valid ? readOwnSpec(name) : undefined;
|
|
115
|
+
if (!spec) {
|
|
116
|
+
const exists = valid && existsSync(join(kernelsDir(), name));
|
|
117
|
+
console.error(exists
|
|
118
|
+
? `uninstall: ${name} wasn't installed by cdp-kernel; leaving it (use jupyter kernelspec remove)`
|
|
119
|
+
: `uninstall: no kernelspec ${name} in ${kernelsDir()}`);
|
|
120
|
+
failed = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
rmSync(spec.dir, { recursive: true, force: true });
|
|
124
|
+
console.log(`removed kernelspec ${name} (${spec.dir})`);
|
|
125
|
+
}
|
|
126
|
+
if (all && names.length === 0)
|
|
127
|
+
console.log(`no cdp-kernel kernelspecs in ${kernelsDir()}`);
|
|
128
|
+
if (failed)
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
52
131
|
const { positionals, values } = parseArgs({
|
|
53
132
|
allowPositionals: true,
|
|
54
133
|
options: {
|
|
@@ -56,7 +135,10 @@ const { positionals, values } = parseArgs({
|
|
|
56
135
|
conn: { type: "string" },
|
|
57
136
|
name: { type: "string" },
|
|
58
137
|
display: { type: "string" },
|
|
138
|
+
all: { type: "boolean", default: false },
|
|
59
139
|
"deno-compat": { type: "boolean", default: false },
|
|
140
|
+
"type-strip": { type: "string", default: "esbuild" },
|
|
141
|
+
"no-language-service": { type: "boolean", default: false },
|
|
60
142
|
},
|
|
61
143
|
});
|
|
62
144
|
const runtime = values.runtime;
|
|
@@ -64,12 +146,18 @@ if (!isRuntime(runtime)) {
|
|
|
64
146
|
console.error(`unknown runtime ${runtime}; expected node, deno, bun, workerd or celld`);
|
|
65
147
|
process.exit(2);
|
|
66
148
|
}
|
|
149
|
+
const typeStrip = values["type-strip"];
|
|
150
|
+
if (!isTypeStripperName(typeStrip)) {
|
|
151
|
+
console.error(`unknown type stripper ${typeStrip}; expected esbuild or sucrase`);
|
|
152
|
+
process.exit(2);
|
|
153
|
+
}
|
|
154
|
+
const languageService = values["no-language-service"] !== true;
|
|
67
155
|
// Deno targets have the real namespace; the shim is for Node.
|
|
68
156
|
const denoCompat = values["deno-compat"] === true && runtime === "node";
|
|
69
157
|
switch (positionals[0]) {
|
|
70
158
|
case "install": {
|
|
71
159
|
const suffix = denoCompat ? "-deno" : "";
|
|
72
|
-
install(runtime, denoCompat, values.name ?? `cdp-${runtime}${suffix}`, values.display ?? `TypeScript (cdp-kernel, ${runtime}${denoCompat ? ", Deno compat" : ""})`);
|
|
160
|
+
install(runtime, denoCompat, typeStrip, languageService, values.name ?? `cdp-${runtime}${suffix}`, values.display ?? `TypeScript (cdp-kernel, ${runtime}${denoCompat ? ", Deno compat" : ""})`);
|
|
73
161
|
break;
|
|
74
162
|
}
|
|
75
163
|
case "kernel": {
|
|
@@ -77,14 +165,20 @@ switch (positionals[0]) {
|
|
|
77
165
|
console.error("kernel: --conn CONNECTION_FILE is required");
|
|
78
166
|
process.exit(2);
|
|
79
167
|
}
|
|
80
|
-
const kernel = new Kernel(parseConnectionInfo(readFileSync(values.conn, "utf8")), runtime, { denoCompat });
|
|
168
|
+
const kernel = new Kernel(parseConnectionInfo(readFileSync(values.conn, "utf8")), runtime, { denoCompat, typeStrip, languageService });
|
|
81
169
|
await kernel.run();
|
|
82
170
|
break;
|
|
83
171
|
}
|
|
172
|
+
case "list":
|
|
173
|
+
list();
|
|
174
|
+
break;
|
|
175
|
+
case "uninstall":
|
|
176
|
+
uninstall([...positionals.slice(1), ...(values.name ? [values.name] : [])], values.all === true);
|
|
177
|
+
break;
|
|
84
178
|
case "version":
|
|
85
179
|
console.log(VERSION);
|
|
86
180
|
break;
|
|
87
181
|
default:
|
|
88
|
-
console.error("usage: cdp-kernel install [--runtime node|deno|bun|workerd|celld] [--deno-compat] | kernel --runtime node|deno|bun|workerd|celld --conn FILE | version");
|
|
182
|
+
console.error("usage: cdp-kernel install [--runtime node|deno|bun|workerd|celld] [--deno-compat] [--type-strip esbuild|sucrase] [--no-language-service] | list | uninstall [NAME...] [--all] | kernel --runtime node|deno|bun|workerd|celld --conn FILE | version");
|
|
89
183
|
process.exit(2);
|
|
90
184
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const PROTOCOL_VERSION = "5.3";
|
|
2
|
+
export type JsonObject = {
|
|
3
|
+
[key: string]: unknown;
|
|
4
|
+
};
|
|
5
|
+
export interface Header extends JsonObject {
|
|
6
|
+
msg_id: string;
|
|
7
|
+
session: string;
|
|
8
|
+
date: string;
|
|
9
|
+
username: string;
|
|
10
|
+
msg_type: string;
|
|
11
|
+
version: string;
|
|
12
|
+
}
|
|
13
|
+
/** A header as received: the fields the kernel relies on, plus whatever else came. */
|
|
14
|
+
export interface ReceivedHeader extends JsonObject {
|
|
15
|
+
msg_id: string;
|
|
16
|
+
msg_type: string;
|
|
17
|
+
}
|
|
18
|
+
/** A message from a frontend, decoded (and on ZeroMQ, verified). */
|
|
19
|
+
export interface KernelMessage {
|
|
20
|
+
header: ReceivedHeader;
|
|
21
|
+
parentHeader: JsonObject;
|
|
22
|
+
metadata: JsonObject;
|
|
23
|
+
content: JsonObject;
|
|
24
|
+
buffers: Uint8Array[];
|
|
25
|
+
}
|
|
26
|
+
/** A message the kernel sends. */
|
|
27
|
+
export interface OutgoingMessage {
|
|
28
|
+
header: Header;
|
|
29
|
+
parentHeader: JsonObject;
|
|
30
|
+
metadata: JsonObject;
|
|
31
|
+
content: JsonObject;
|
|
32
|
+
buffers: Uint8Array[];
|
|
33
|
+
}
|
|
34
|
+
export declare function makeHeader(session: string, msgType: string): Header;
|
package/dist/message.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Jupyter message shapes and headers, without the wire format: what the
|
|
2
|
+
// kernel's message handling (src/kernel-core.ts) builds and reads. The
|
|
3
|
+
// framing and HMAC signing that ZeroMQ peers need are in src/wire.ts.
|
|
4
|
+
export const PROTOCOL_VERSION = "5.3";
|
|
5
|
+
export function makeHeader(session, msgType) {
|
|
6
|
+
return {
|
|
7
|
+
msg_id: crypto.randomUUID(),
|
|
8
|
+
session,
|
|
9
|
+
date: new Date().toISOString(),
|
|
10
|
+
username: "kernel",
|
|
11
|
+
msg_type: msgType,
|
|
12
|
+
version: PROTOCOL_VERSION,
|
|
13
|
+
};
|
|
14
|
+
}
|
package/dist/mod.d.ts
CHANGED
|
@@ -22,4 +22,5 @@
|
|
|
22
22
|
*/
|
|
23
23
|
export { type ConnectionInfo, Kernel, parseConnectionInfo } from "./kernel.ts";
|
|
24
24
|
export type { RuntimeName } from "./target.ts";
|
|
25
|
+
export { CellSyntaxError, type CellTransformOptions, type JsxOptions, type TypeStripper } from "./transform.ts";
|
|
25
26
|
export { VERSION } from "./package.ts";
|
package/dist/mod.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type TS from "typescript";
|
|
2
|
+
import type { DenoResolver } from "./deno-types.ts";
|
|
3
|
+
export interface NotebookTypesOptions {
|
|
4
|
+
notebookDir: string;
|
|
5
|
+
/**
|
|
6
|
+
* The installer cache npm: and jsr: imports resolve from: where the
|
|
7
|
+
* target installs them (Bun's own for Bun, npm's for the rest). Null for
|
|
8
|
+
* Deno targets, which resolve them natively (see `deno`).
|
|
9
|
+
*/
|
|
10
|
+
npmCache: "npm" | "bun" | null;
|
|
11
|
+
/**
|
|
12
|
+
* For Deno targets: where Deno put what the executed cells imported
|
|
13
|
+
* (npm:, jsr:, URLs, the project's import map).
|
|
14
|
+
*/
|
|
15
|
+
deno?: DenoResolver;
|
|
16
|
+
/** compilerOptions.types: type packages found from the notebook's directory. */
|
|
17
|
+
types: string[];
|
|
18
|
+
/** Declaration files every cell sees, e.g. runtime types. */
|
|
19
|
+
typeFiles: string[];
|
|
20
|
+
/** Declarations for the kernel's own globals (jupyter, prompt, confirm). */
|
|
21
|
+
globals: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CompletionEntry {
|
|
24
|
+
name: string;
|
|
25
|
+
/** TypeScript's ScriptElementKind: "method", "property", "const", ... */
|
|
26
|
+
kind: string;
|
|
27
|
+
}
|
|
28
|
+
export interface CompletionResult {
|
|
29
|
+
/** Where the completed name starts in the cell, as a UTF-16 index. */
|
|
30
|
+
start: number;
|
|
31
|
+
entries: CompletionEntry[];
|
|
32
|
+
}
|
|
33
|
+
export declare class NotebookTypes {
|
|
34
|
+
#private;
|
|
35
|
+
constructor(ts: typeof TS, options: NotebookTypesOptions);
|
|
36
|
+
/** An executed cell: its declarations are in scope for later ones. */
|
|
37
|
+
addCell(code: string): void;
|
|
38
|
+
/** The runtime restarted and lost its state. */
|
|
39
|
+
reset(): void;
|
|
40
|
+
/** Adds declaration files, e.g. runtime types installed after startup. */
|
|
41
|
+
addTypeFiles(files: string[]): void;
|
|
42
|
+
/** Adds a declaration file held in memory, e.g. the output of `deno types`. */
|
|
43
|
+
addVirtualTypeFile(name: string, text: string): void;
|
|
44
|
+
/** Adds type packages (compilerOptions.types) that resolve from the notebook. */
|
|
45
|
+
addTypeNames(names: string[]): void;
|
|
46
|
+
/** Whether a type package (compilerOptions.types) resolves from the notebook. */
|
|
47
|
+
resolvesTypes(name: string): boolean;
|
|
48
|
+
/** Completions at `cursor` (a UTF-16 index) in `code`, the cell being edited. */
|
|
49
|
+
complete(code: string, cursor: number): CompletionResult;
|
|
50
|
+
/**
|
|
51
|
+
* The type, signature and JSDoc of the name at `cursor`: the one the
|
|
52
|
+
* cursor is in or just after, or, right after "name(", that callee.
|
|
53
|
+
*/
|
|
54
|
+
quickInfo(code: string, cursor: number): string | null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Declarations for the names the kernel adds to every target, as in
|
|
58
|
+
* src/globals.ts. `display` is runtime/display.mjs's path; its sibling
|
|
59
|
+
* display.d.mts types `jupyter`. Without it (a kernel run from JSR, whose
|
|
60
|
+
* runtime files aren't local), `jupyter` is untyped.
|
|
61
|
+
*/
|
|
62
|
+
export declare function kernelGlobals(display: string | null): string;
|