@tlockney/cdp-kernel 0.5.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 +27 -6
- package/dist/cdp.d.ts +2 -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/deno-types.d.ts +30 -0
- package/dist/deno-types.js +93 -0
- package/dist/kernel.d.ts +7 -1
- package/dist/kernel.js +187 -29
- 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 +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.d.ts +27 -3
- package/dist/target.js +193 -36
- package/dist/transform.d.ts +21 -1
- package/dist/transform.js +38 -42
- package/dist/workerd-target.js +1 -1
- package/dist/workers-project.js +13 -2
- package/dist/zmtp.d.ts +2 -0
- package/dist/zmtp.js +12 -0
- package/package.json +9 -4
- package/runtime/bootstrap.mjs +113 -33
- package/runtime/introspect.mjs +1 -1
- package/runtime/node-hooks.d.mts +6 -0
- package/runtime/node-hooks.mjs +11 -0
package/README.md
CHANGED
|
@@ -10,9 +10,19 @@ inspector speaks CDP works too.
|
|
|
10
10
|
**Website:** <https://cdp-kernel.org/> ·
|
|
11
11
|
**Docs:** [cdp-kernel.org/docs](https://cdp-kernel.org/docs/)
|
|
12
12
|
|
|
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)
|
|
15
|
+
|
|
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)).
|
|
19
|
+
|
|
13
20
|
This is early software. It passes a conformance suite derived from Deno's
|
|
14
21
|
own Jupyter integration tests on every target, and it's packaged for npm and
|
|
15
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.
|
|
16
26
|
|
|
17
27
|
## Features
|
|
18
28
|
|
|
@@ -29,7 +39,7 @@ JSR as `@tlockney/cdp-kernel`, but it hasn't seen much real use yet.
|
|
|
29
39
|
|
|
30
40
|
## Install
|
|
31
41
|
|
|
32
|
-
Node
|
|
42
|
+
Node 22.18+ host, from npm:
|
|
33
43
|
|
|
34
44
|
```sh
|
|
35
45
|
npm install -g @tlockney/cdp-kernel
|
|
@@ -43,11 +53,19 @@ deno run -A jsr:@tlockney/cdp-kernel/cli install --runtime deno
|
|
|
43
53
|
```
|
|
44
54
|
|
|
45
55
|
Each install writes a Jupyter kernelspec (`cdp-node`, `cdp-deno`, …). Pick
|
|
46
|
-
it in JupyterLab or VS Code.
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
49
61
|
requirements, and installing from a checkout.
|
|
50
62
|
|
|
63
|
+
JupyterLab with every runtime and the examples, in a container:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
docker run --rm -p 8888:8888 ghcr.io/tlockney/cdp-kernel
|
|
67
|
+
```
|
|
68
|
+
|
|
51
69
|
## Quick start
|
|
52
70
|
|
|
53
71
|
```ts
|
|
@@ -68,10 +86,13 @@ New to Jupyter? Start with
|
|
|
68
86
|
[Widgets and comms](https://cdp-kernel.org/docs/widgets),
|
|
69
87
|
[Debugger](https://cdp-kernel.org/docs/debugger)
|
|
70
88
|
- Reference: [Editor types](https://cdp-kernel.org/docs/editor-types),
|
|
71
|
-
[Known gaps](https://cdp-kernel.org/docs/known-gaps)
|
|
89
|
+
[Known gaps](https://cdp-kernel.org/docs/known-gaps),
|
|
90
|
+
[Support policy](https://cdp-kernel.org/docs/support)
|
|
72
91
|
- Internals: [Architecture](https://cdp-kernel.org/docs/architecture),
|
|
73
92
|
[Runtime integrations](https://cdp-kernel.org/docs/integrations)
|
|
74
|
-
- Contributing: [
|
|
93
|
+
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md),
|
|
94
|
+
[Development](https://cdp-kernel.org/docs/development),
|
|
95
|
+
[Security policy](SECURITY.md)
|
|
75
96
|
|
|
76
97
|
The [feasibility study](https://cdp-kernel.org/docs/feasibility-study)
|
|
77
98
|
behind the project covers the design, the alternatives considered (workerd,
|
package/dist/cdp.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare class CdpSession {
|
|
|
33
33
|
static connect(url: string): Promise<CdpSession>;
|
|
34
34
|
on(method: string, listener: (params: CdpParams) => void): void;
|
|
35
35
|
send(method: string, params?: CdpParams): Promise<CdpParams>;
|
|
36
|
+
/** In-flight calls, oldest first, with how long each has waited: for a state dump (#237). */
|
|
37
|
+
pendingCalls(): string[];
|
|
36
38
|
/** Reject every in-flight call; their late responses are then ignored. */
|
|
37
39
|
abandonPending(): void;
|
|
38
40
|
close(): void;
|
package/dist/cdp.js
CHANGED
|
@@ -69,10 +69,15 @@ export class CdpSession {
|
|
|
69
69
|
return Promise.reject(new Error("CDP connection closed"));
|
|
70
70
|
const id = ++this.#nextId;
|
|
71
71
|
return new Promise((resolve, reject) => {
|
|
72
|
-
this.#pending.set(id, { method, resolve, reject });
|
|
72
|
+
this.#pending.set(id, { method, sent: Date.now(), resolve, reject });
|
|
73
73
|
this.#ws.send(JSON.stringify({ id, method, params }));
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
+
/** In-flight calls, oldest first, with how long each has waited: for a state dump (#237). */
|
|
77
|
+
pendingCalls() {
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
return [...this.#pending.values()].map((p) => `${p.method} (${now - p.sent} ms)`);
|
|
80
|
+
}
|
|
76
81
|
/** Reject every in-flight call; their late responses are then ignored. */
|
|
77
82
|
abandonPending() {
|
|
78
83
|
for (const pending of this.#pending.values()) {
|
package/dist/celld-target.js
CHANGED
|
@@ -24,7 +24,7 @@ import { build } from "esbuild";
|
|
|
24
24
|
import { isRecord } from "./cdp.js";
|
|
25
25
|
import { parseJsonc } from "./deno-config.js";
|
|
26
26
|
import { runtimeDir } from "./package.js";
|
|
27
|
-
import { killOnExit } from "./children.js";
|
|
27
|
+
import { childPids, killOnExit, reapWith } from "./children.js";
|
|
28
28
|
import { cellMeta, WorkerdTarget } from "./workerd-target.js";
|
|
29
29
|
import { builtinAliases, findWranglerConfig } from "./workers-project.js";
|
|
30
30
|
/** The project keys the generated config keeps: bindings and compatibility. */
|
|
@@ -122,16 +122,10 @@ function channelOf(ws) {
|
|
|
122
122
|
},
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
return []; // none
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/** Kills celld dev's node, then the supervisor itself. */
|
|
125
|
+
/**
|
|
126
|
+
* Kills celld dev's own children, which run in their own process groups,
|
|
127
|
+
* then the supervisor itself.
|
|
128
|
+
*/
|
|
135
129
|
function killTree(child) {
|
|
136
130
|
for (const pid of childPids(child.pid ?? -1)) {
|
|
137
131
|
try {
|
|
@@ -222,7 +216,7 @@ class CelldHost {
|
|
|
222
216
|
killOnExit(child, () => {
|
|
223
217
|
killTree(child);
|
|
224
218
|
removeFiles();
|
|
225
|
-
});
|
|
219
|
+
}, { dir: projectConfig === null ? dir : undefined });
|
|
226
220
|
// Cell output is captured in the driver; celld's own lines go to the
|
|
227
221
|
// server log.
|
|
228
222
|
// `early` keeps that output for a startup error only, and stops growing
|
|
@@ -262,6 +256,9 @@ class CelldHost {
|
|
|
262
256
|
})();
|
|
263
257
|
try {
|
|
264
258
|
const host = new CelldHost(child, await Promise.race([ready, exited]), `celld ${version}`.trim(), projectConfig ?? undefined);
|
|
259
|
+
// Its server, in its own process group, outlives a SIGKILLed celld dev.
|
|
260
|
+
for (const pid of childPids(child.pid ?? -1))
|
|
261
|
+
reapWith(pid, child);
|
|
265
262
|
starting = false;
|
|
266
263
|
early = "";
|
|
267
264
|
return host;
|
package/dist/children.d.ts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ChildProcess } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* The helper's script. Lines on stdin: `+pid` registers a pid, `-pid`
|
|
4
|
+
* forgets it (it exited, and the pid may be reused), `d<path>` registers a
|
|
5
|
+
* directory. In its own session, so a killpg of this process's group (as
|
|
6
|
+
* Jupyter's kill does) doesn't take it down first.
|
|
7
|
+
*/
|
|
8
|
+
export declare const REAPER_SCRIPT = "trap '' HUP INT\npids=\" \"\ndirs=\"\"\nwhile IFS= read -r line; do\n case $line in\n +*) pids=\"$pids${line#?} \" ;;\n -*) pids=$(printf '%s' \"$pids\" | sed \"s/ ${line#?} / /\") ;;\n d*) dirs=\"$dirs${line#?}\n\" ;;\n esac\ndone\nkill -9 $pids 2>/dev/null\nprintf '%s' \"$dirs\" | while IFS= read -r dir; do rm -rf -- \"$dir\"; done\n";
|
|
9
|
+
/**
|
|
10
|
+
* Kills `pid` if this process dies, until the returned function is called
|
|
11
|
+
* (when the process is known to have exited: its pid may be reused).
|
|
12
|
+
*/
|
|
13
|
+
export declare function reap(pid: number): () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Kills `pid`, which isn't this process's child (a runtime's own child in
|
|
16
|
+
* its own process group), if this process dies while `owner` runs.
|
|
17
|
+
*/
|
|
18
|
+
export declare function reapWith(pid: number, owner: ChildProcess): void;
|
|
19
|
+
/** `pid`'s children whose command line matches `pattern` (pgrep's -f). */
|
|
20
|
+
export declare function childPids(pid: number, pattern?: string): number[];
|
|
2
21
|
/**
|
|
3
22
|
* Kills `child` if it's still running when this process exits. `cleanup`
|
|
4
23
|
* replaces the default SIGKILL; it must be synchronous, as exit hooks are,
|
|
5
|
-
* and do whatever the child's own exit listener would have done.
|
|
24
|
+
* and do whatever the child's own exit listener would have done. If this
|
|
25
|
+
* process is SIGKILLed, the helper kills `child` and removes `dir`.
|
|
6
26
|
*/
|
|
7
|
-
export declare function killOnExit(child: ChildProcess, cleanup?: () => void
|
|
27
|
+
export declare function killOnExit(child: ChildProcess, cleanup?: () => void, { dir }?: {
|
|
28
|
+
dir?: string;
|
|
29
|
+
}): void;
|
package/dist/children.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
// Runtimes this process started, killed when it exits however it exits
|
|
2
2
|
// short of SIGKILL: a target left behind keeps running, reparented to pid 1,
|
|
3
3
|
// with whatever temp directory it uses (#127, #218).
|
|
4
|
+
//
|
|
5
|
+
// SIGKILL runs no exit hook, so a helper outside this process also knows the
|
|
6
|
+
// runtimes' pids (#240): a detached sh reading this process's pipe, which
|
|
7
|
+
// kills whatever is still registered, and removes the registered temp
|
|
8
|
+
// directories, once the pipe closes. That covers what nothing in a runtime
|
|
9
|
+
// can notice: celld dev and its server, and a Node or Deno runtime stuck in
|
|
10
|
+
// a cell's synchronous loop, where the #219 watchdog never gets to run.
|
|
4
11
|
import process from "node:process";
|
|
12
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
5
13
|
const cleanups = new Set();
|
|
6
14
|
function runAll() {
|
|
7
15
|
for (const cleanup of cleanups) {
|
|
@@ -15,12 +23,82 @@ function runAll() {
|
|
|
15
23
|
cleanups.clear();
|
|
16
24
|
}
|
|
17
25
|
let hooked = false;
|
|
26
|
+
/**
|
|
27
|
+
* The helper's script. Lines on stdin: `+pid` registers a pid, `-pid`
|
|
28
|
+
* forgets it (it exited, and the pid may be reused), `d<path>` registers a
|
|
29
|
+
* directory. In its own session, so a killpg of this process's group (as
|
|
30
|
+
* Jupyter's kill does) doesn't take it down first.
|
|
31
|
+
*/
|
|
32
|
+
export const REAPER_SCRIPT = `trap '' HUP INT
|
|
33
|
+
pids=" "
|
|
34
|
+
dirs=""
|
|
35
|
+
while IFS= read -r line; do
|
|
36
|
+
case $line in
|
|
37
|
+
+*) pids="$pids\${line#?} " ;;
|
|
38
|
+
-*) pids=$(printf '%s' "$pids" | sed "s/ \${line#?} / /") ;;
|
|
39
|
+
d*) dirs="$dirs\${line#?}
|
|
40
|
+
" ;;
|
|
41
|
+
esac
|
|
42
|
+
done
|
|
43
|
+
kill -9 $pids 2>/dev/null
|
|
44
|
+
printf '%s' "$dirs" | while IFS= read -r dir; do rm -rf -- "$dir"; done
|
|
45
|
+
`;
|
|
46
|
+
let reaper;
|
|
47
|
+
/** Sends `line` to the helper, starting it first; a no-op on Windows. */
|
|
48
|
+
function tellReaper(line) {
|
|
49
|
+
if (reaper === undefined) {
|
|
50
|
+
reaper = null;
|
|
51
|
+
if (process.platform === "win32")
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
const child = spawn("/bin/sh", ["-c", REAPER_SCRIPT], { cwd: "/", detached: true, stdio: ["pipe", "ignore", "ignore"] });
|
|
55
|
+
child.once("error", () => (reaper = null));
|
|
56
|
+
child.stdin?.on("error", () => (reaper = null));
|
|
57
|
+
// Neither the helper nor its pipe keeps this process running.
|
|
58
|
+
child.unref();
|
|
59
|
+
child.stdin?.unref?.();
|
|
60
|
+
reaper = child;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
reaper?.stdin?.write(`${line}\n`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Kills `pid` if this process dies, until the returned function is called
|
|
70
|
+
* (when the process is known to have exited: its pid may be reused).
|
|
71
|
+
*/
|
|
72
|
+
export function reap(pid) {
|
|
73
|
+
tellReaper(`+${pid}`);
|
|
74
|
+
return () => tellReaper(`-${pid}`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Kills `pid`, which isn't this process's child (a runtime's own child in
|
|
78
|
+
* its own process group), if this process dies while `owner` runs.
|
|
79
|
+
*/
|
|
80
|
+
export function reapWith(pid, owner) {
|
|
81
|
+
if (owner.exitCode !== null || owner.signalCode !== null)
|
|
82
|
+
return;
|
|
83
|
+
owner.once("exit", reap(pid));
|
|
84
|
+
}
|
|
85
|
+
/** `pid`'s children whose command line matches `pattern` (pgrep's -f). */
|
|
86
|
+
export function childPids(pid, pattern) {
|
|
87
|
+
try {
|
|
88
|
+
const args = ["-P", String(pid), ...(pattern === undefined ? [] : ["-f", pattern])];
|
|
89
|
+
return execFileSync("pgrep", args, { encoding: "utf8" }).split(/\s+/).filter(Boolean).map(Number);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return []; // none, or no pgrep
|
|
93
|
+
}
|
|
94
|
+
}
|
|
18
95
|
/**
|
|
19
96
|
* Kills `child` if it's still running when this process exits. `cleanup`
|
|
20
97
|
* replaces the default SIGKILL; it must be synchronous, as exit hooks are,
|
|
21
|
-
* and do whatever the child's own exit listener would have done.
|
|
98
|
+
* and do whatever the child's own exit listener would have done. If this
|
|
99
|
+
* process is SIGKILLed, the helper kills `child` and removes `dir`.
|
|
22
100
|
*/
|
|
23
|
-
export function killOnExit(child, cleanup = () => child.kill("SIGKILL")) {
|
|
101
|
+
export function killOnExit(child, cleanup = () => child.kill("SIGKILL"), { dir } = {}) {
|
|
24
102
|
if (!hooked) {
|
|
25
103
|
hooked = true;
|
|
26
104
|
process.on("exit", runAll);
|
|
@@ -29,4 +107,8 @@ export function killOnExit(child, cleanup = () => child.kill("SIGKILL")) {
|
|
|
29
107
|
return;
|
|
30
108
|
cleanups.add(cleanup);
|
|
31
109
|
child.once("exit", () => cleanups.delete(cleanup));
|
|
110
|
+
if (dir !== undefined)
|
|
111
|
+
tellReaper(`d${dir}`);
|
|
112
|
+
if (child.pid !== undefined)
|
|
113
|
+
reapWith(child.pid, child);
|
|
32
114
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type DenoResolution = {
|
|
2
|
+
kind: "npm";
|
|
3
|
+
dir: string;
|
|
4
|
+
subpath: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "remote";
|
|
7
|
+
path: string;
|
|
8
|
+
};
|
|
9
|
+
export type RunDeno = (args: string[]) => {
|
|
10
|
+
status: number | null;
|
|
11
|
+
stdout: string;
|
|
12
|
+
};
|
|
13
|
+
export declare class DenoResolver {
|
|
14
|
+
#private;
|
|
15
|
+
/** Where remote modules appear to the language service. */
|
|
16
|
+
readonly root: string;
|
|
17
|
+
constructor({ deno, config, root }: {
|
|
18
|
+
deno?: string;
|
|
19
|
+
config?: string | null;
|
|
20
|
+
root: string;
|
|
21
|
+
}, run?: RunDeno);
|
|
22
|
+
/** The virtual path for a remote module's URL, e.g. <root>/https/jsr.io/@std/path/1.1.6/mod.ts. */
|
|
23
|
+
pathFor(url: string): string | null;
|
|
24
|
+
/** The cached file behind a virtual path, if a lookup has found it. */
|
|
25
|
+
localFile(path: string): string | undefined;
|
|
26
|
+
/** Where `specifier` resolves for Deno, from what's already in its cache. */
|
|
27
|
+
lookup(specifier: string): DenoResolution | null;
|
|
28
|
+
}
|
|
29
|
+
/** `deno types`: declarations for the Deno namespace and its web globals. */
|
|
30
|
+
export declare function denoTypes(run?: RunDeno): string | null;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Types for a Deno target's imports (#275). Deno resolves npm:, jsr:, URLs
|
|
2
|
+
// and its project's import map itself and keeps what it fetched in its own
|
|
3
|
+
// cache, so the language service asks it where a specifier went:
|
|
4
|
+
// `deno info --json` over a one-line module importing it. npm packages map
|
|
5
|
+
// to their directory in Deno's npm cache; remote modules (jsr:, https:)
|
|
6
|
+
// are stored under hashed names without an extension, so the language
|
|
7
|
+
// service sees each under a virtual path built from its URL, which keeps
|
|
8
|
+
// relative imports between them working.
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
function runDeno(deno) {
|
|
14
|
+
return (args) => {
|
|
15
|
+
const result = spawnSync(deno, args, { encoding: "utf8", timeout: 30_000, maxBuffer: 64 * 1024 * 1024 });
|
|
16
|
+
return { status: result.status, stdout: result.stdout ?? "" };
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export class DenoResolver {
|
|
20
|
+
#run;
|
|
21
|
+
#config;
|
|
22
|
+
/** Where remote modules appear to the language service. */
|
|
23
|
+
root;
|
|
24
|
+
#lookups = new Map();
|
|
25
|
+
/** Virtual path -> the cached file holding that URL's module. */
|
|
26
|
+
#remote = new Map();
|
|
27
|
+
constructor({ deno = "deno", config = null, root }, run = runDeno(deno)) {
|
|
28
|
+
this.#run = run;
|
|
29
|
+
this.#config = config;
|
|
30
|
+
this.root = root;
|
|
31
|
+
}
|
|
32
|
+
/** The virtual path for a remote module's URL, e.g. <root>/https/jsr.io/@std/path/1.1.6/mod.ts. */
|
|
33
|
+
pathFor(url) {
|
|
34
|
+
const m = /^(https?):\/\/(.+)$/.exec(url);
|
|
35
|
+
return m ? join(this.root, m[1], m[2]) : null;
|
|
36
|
+
}
|
|
37
|
+
/** The cached file behind a virtual path, if a lookup has found it. */
|
|
38
|
+
localFile(path) {
|
|
39
|
+
return this.#remote.get(path);
|
|
40
|
+
}
|
|
41
|
+
/** Where `specifier` resolves for Deno, from what's already in its cache. */
|
|
42
|
+
lookup(specifier) {
|
|
43
|
+
if (this.#lookups.has(specifier))
|
|
44
|
+
return this.#lookups.get(specifier) ?? null;
|
|
45
|
+
const resolution = this.#lookup(specifier);
|
|
46
|
+
this.#lookups.set(specifier, resolution);
|
|
47
|
+
return resolution;
|
|
48
|
+
}
|
|
49
|
+
#lookup(specifier) {
|
|
50
|
+
const dir = mkdtempSync(join(tmpdir(), "cdpk-deno-info-"));
|
|
51
|
+
try {
|
|
52
|
+
const entry = join(dir, "entry.ts");
|
|
53
|
+
writeFileSync(entry, `import ${JSON.stringify(specifier)};\n`);
|
|
54
|
+
const flags = this.#config === null ? ["--node-modules-dir=none"] : ["--config", this.#config];
|
|
55
|
+
const result = this.#run(["info", "--json", "--no-lock", ...flags, entry]);
|
|
56
|
+
if (result.status !== 0)
|
|
57
|
+
return null;
|
|
58
|
+
return this.#read(JSON.parse(result.stdout));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
rmSync(dir, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
#read(info) {
|
|
68
|
+
for (const mod of info.modules) {
|
|
69
|
+
const path = mod.local === undefined ? null : this.pathFor(mod.specifier);
|
|
70
|
+
if (path !== null && mod.local !== undefined)
|
|
71
|
+
this.#remote.set(path, mod.local);
|
|
72
|
+
}
|
|
73
|
+
const root = info.modules.find((m) => m.specifier === info.roots[0]);
|
|
74
|
+
const dep = root?.dependencies?.[0];
|
|
75
|
+
let target = dep?.code?.specifier ?? dep?.specifier;
|
|
76
|
+
if (target === undefined)
|
|
77
|
+
return null;
|
|
78
|
+
for (let i = 0; i < 10 && info.redirects[target] !== undefined; i++)
|
|
79
|
+
target = info.redirects[target];
|
|
80
|
+
const npm = /^npm:\/?((?:@[^/]+\/)?[^/@]+@[^/]+)(\/.*)?$/.exec(target);
|
|
81
|
+
if (npm) {
|
|
82
|
+
const dir = info.npmPackages[npm[1]]?.localPath;
|
|
83
|
+
return dir === undefined ? null : { kind: "npm", dir, subpath: npm[2] ?? "" };
|
|
84
|
+
}
|
|
85
|
+
const path = this.pathFor(target);
|
|
86
|
+
return path !== null && this.#remote.has(path) ? { kind: "remote", path } : null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** `deno types`: declarations for the Deno namespace and its web globals. */
|
|
90
|
+
export function denoTypes(run = runDeno("deno")) {
|
|
91
|
+
const result = run(["types"]);
|
|
92
|
+
return result.status === 0 ? result.stdout : null;
|
|
93
|
+
}
|
package/dist/kernel.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RuntimeName } from "./target.ts";
|
|
2
|
+
import { type TypeStripper, type TypeStripperName } from "./transform.ts";
|
|
2
3
|
export interface ConnectionInfo {
|
|
3
4
|
ip: string;
|
|
4
5
|
key: string;
|
|
@@ -14,8 +15,13 @@ export declare function parseConnectionInfo(json: string): ConnectionInfo;
|
|
|
14
15
|
export declare class Kernel {
|
|
15
16
|
#private;
|
|
16
17
|
constructor(conn: ConnectionInfo, runtime: RuntimeName, options?: {
|
|
17
|
-
denoCompat?: boolean;
|
|
18
|
+
denoCompat?: boolean;
|
|
19
|
+
/** Defaults to the process cwd, which Jupyter sets. */
|
|
18
20
|
notebookDir?: string;
|
|
21
|
+
/** What strips cells' types; esbuild by default (#238). */
|
|
22
|
+
typeStrip?: TypeStripperName | TypeStripper;
|
|
23
|
+
/** Typed completion from TypeScript's language service; on by default (#273). */
|
|
24
|
+
languageService?: boolean;
|
|
19
25
|
});
|
|
20
26
|
run(): Promise<void>;
|
|
21
27
|
}
|