mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
fix(cli/js/process): Strengthen socket types based on pipes (#4836)
This commit is contained in:
parent
54742d29dc
commit
b3e189ee4f
9 changed files with 61 additions and 54 deletions
10
cli/js/lib.deno.ns.d.ts
vendored
10
cli/js/lib.deno.ns.d.ts
vendored
|
@ -1741,12 +1741,12 @@ declare namespace Deno {
|
|||
options?: { recursive: boolean }
|
||||
): AsyncIterableIterator<FsEvent>;
|
||||
|
||||
export class Process {
|
||||
export class Process<T extends RunOptions = RunOptions> {
|
||||
readonly rid: number;
|
||||
readonly pid: number;
|
||||
readonly stdin?: Writer & Closer;
|
||||
readonly stdout?: Reader & Closer;
|
||||
readonly stderr?: Reader & Closer;
|
||||
readonly stdin: T["stdin"] extends "piped" ? Writer & Closer : null;
|
||||
readonly stdout: T["stdout"] extends "piped" ? Reader & Closer : null;
|
||||
readonly stderr: T["stderr"] extends "piped" ? Reader & Closer : null;
|
||||
/** Resolves to the current status of the process. */
|
||||
status(): Promise<ProcessStatus>;
|
||||
/** Buffer the stdout until EOF and return it as `Uint8Array`.
|
||||
|
@ -1829,7 +1829,7 @@ declare namespace Deno {
|
|||
* Details of the spawned process are returned.
|
||||
*
|
||||
* Requires `allow-run` permission. */
|
||||
export function run(opt: RunOptions): Process;
|
||||
export function run<T extends RunOptions = RunOptions>(opt: T): Process<T>;
|
||||
|
||||
interface InspectOptions {
|
||||
depth?: number;
|
||||
|
|
|
@ -5,17 +5,15 @@ import { Closer, Reader, Writer } from "./io.ts";
|
|||
import { readAll } from "./buffer.ts";
|
||||
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
|
||||
|
||||
export type ProcessStdio = "inherit" | "piped" | "null";
|
||||
|
||||
// TODO Maybe extend VSCode's 'CommandOptions'?
|
||||
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
|
||||
export interface RunOptions {
|
||||
cmd: string[];
|
||||
cwd?: string;
|
||||
env?: { [key: string]: string };
|
||||
stdout?: ProcessStdio | number;
|
||||
stderr?: ProcessStdio | number;
|
||||
stdin?: ProcessStdio | number;
|
||||
stdout?: "inherit" | "piped" | "null" | number;
|
||||
stderr?: "inherit" | "piped" | "null" | number;
|
||||
stdin?: "inherit" | "piped" | "null" | number;
|
||||
}
|
||||
|
||||
async function runStatus(rid: number): Promise<ProcessStatus> {
|
||||
|
@ -30,12 +28,12 @@ async function runStatus(rid: number): Promise<ProcessStatus> {
|
|||
}
|
||||
}
|
||||
|
||||
export class Process {
|
||||
export class Process<T extends RunOptions = RunOptions> {
|
||||
readonly rid: number;
|
||||
readonly pid: number;
|
||||
readonly stdin?: Writer & Closer;
|
||||
readonly stdout?: Reader & Closer;
|
||||
readonly stderr?: Reader & Closer;
|
||||
readonly stdin!: T["stdin"] extends "piped" ? Writer & Closer : null;
|
||||
readonly stdout!: T["stdout"] extends "piped" ? Reader & Closer : null;
|
||||
readonly stderr!: T["stderr"] extends "piped" ? Reader & Closer : null;
|
||||
|
||||
// @internal
|
||||
constructor(res: RunResponse) {
|
||||
|
@ -43,15 +41,19 @@ export class Process {
|
|||
this.pid = res.pid;
|
||||
|
||||
if (res.stdinRid && res.stdinRid > 0) {
|
||||
this.stdin = new File(res.stdinRid);
|
||||
this.stdin = (new File(res.stdinRid) as unknown) as Process<T>["stdin"];
|
||||
}
|
||||
|
||||
if (res.stdoutRid && res.stdoutRid > 0) {
|
||||
this.stdout = new File(res.stdoutRid);
|
||||
this.stdout = (new File(res.stdoutRid) as unknown) as Process<
|
||||
T
|
||||
>["stdout"];
|
||||
}
|
||||
|
||||
if (res.stderrRid && res.stderrRid > 0) {
|
||||
this.stderr = new File(res.stderrRid);
|
||||
this.stderr = (new File(res.stderrRid) as unknown) as Process<
|
||||
T
|
||||
>["stderr"];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,23 +63,23 @@ export class Process {
|
|||
|
||||
async output(): Promise<Uint8Array> {
|
||||
if (!this.stdout) {
|
||||
throw new Error("Process.output: stdout is undefined");
|
||||
throw new TypeError("stdout was not piped");
|
||||
}
|
||||
try {
|
||||
return await readAll(this.stdout);
|
||||
return await readAll(this.stdout as Reader & Closer);
|
||||
} finally {
|
||||
this.stdout.close();
|
||||
(this.stdout as Reader & Closer).close();
|
||||
}
|
||||
}
|
||||
|
||||
async stderrOutput(): Promise<Uint8Array> {
|
||||
if (!this.stderr) {
|
||||
throw new Error("Process.stderrOutput: stderr is undefined");
|
||||
throw new TypeError("stderr was not piped");
|
||||
}
|
||||
try {
|
||||
return await readAll(this.stderr);
|
||||
return await readAll(this.stderr as Reader & Closer);
|
||||
} finally {
|
||||
this.stderr.close();
|
||||
(this.stderr as Reader & Closer).close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,14 +109,15 @@ interface RunResponse {
|
|||
stdoutRid: number | null;
|
||||
stderrRid: number | null;
|
||||
}
|
||||
export function run({
|
||||
|
||||
export function run<T extends RunOptions = RunOptions>({
|
||||
cmd,
|
||||
cwd = undefined,
|
||||
env = {},
|
||||
stdout = "inherit",
|
||||
stderr = "inherit",
|
||||
stdin = "inherit",
|
||||
}: RunOptions): Process {
|
||||
}: T): Process<T> {
|
||||
const res = runOp({
|
||||
cmd: cmd.map(String),
|
||||
cwd,
|
||||
|
@ -126,5 +129,5 @@ export function run({
|
|||
stdoutRid: isRid(stdout) ? stdout : 0,
|
||||
stderrRid: isRid(stderr) ? stderr : 0,
|
||||
}) as RunResponse;
|
||||
return new Process(res);
|
||||
return new Process<T>(res);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ unitTest({ perms: { run: true } }, async function runSuccess(): Promise<void> {
|
|||
assertEquals(status.success, true);
|
||||
assertEquals(status.code, 0);
|
||||
assertEquals(status.signal, undefined);
|
||||
p.stdout!.close();
|
||||
p.stdout.close();
|
||||
p.close();
|
||||
});
|
||||
|
||||
|
@ -141,7 +141,7 @@ unitTest({ perms: { run: true } }, async function runStdinPiped(): Promise<
|
|||
const n = await p.stdin.write(msg);
|
||||
assertEquals(n, msg.byteLength);
|
||||
|
||||
p.stdin!.close();
|
||||
p.stdin.close();
|
||||
|
||||
const status = await p.status();
|
||||
assertEquals(status.success, true);
|
||||
|
@ -161,16 +161,16 @@ unitTest({ perms: { run: true } }, async function runStdoutPiped(): Promise<
|
|||
assert(!p.stderr);
|
||||
|
||||
const data = new Uint8Array(10);
|
||||
let r = await p.stdout!.read(data);
|
||||
let r = await p.stdout.read(data);
|
||||
if (r === null) {
|
||||
throw new Error("p.stdout.read(...) should not be null");
|
||||
}
|
||||
assertEquals(r, 5);
|
||||
const s = new TextDecoder().decode(data.subarray(0, r));
|
||||
assertEquals(s, "hello");
|
||||
r = await p.stdout!.read(data);
|
||||
r = await p.stdout.read(data);
|
||||
assertEquals(r, null);
|
||||
p.stdout!.close();
|
||||
p.stdout.close();
|
||||
|
||||
const status = await p.status();
|
||||
assertEquals(status.success, true);
|
||||
|
@ -190,14 +190,14 @@ unitTest({ perms: { run: true } }, async function runStderrPiped(): Promise<
|
|||
assert(!p.stdout);
|
||||
|
||||
const data = new Uint8Array(10);
|
||||
let r = await p.stderr!.read(data);
|
||||
let r = await p.stderr.read(data);
|
||||
if (r === null) {
|
||||
throw new Error("p.stderr.read should not return null here");
|
||||
}
|
||||
assertEquals(r, 5);
|
||||
const s = new TextDecoder().decode(data.subarray(0, r));
|
||||
assertEquals(s, "hello");
|
||||
r = await p.stderr!.read(data);
|
||||
r = await p.stderr.read(data);
|
||||
assertEquals(r, null);
|
||||
p.stderr!.close();
|
||||
|
||||
|
@ -320,9 +320,9 @@ unitTest({ perms: { run: true } }, async function runClose(): Promise<void> {
|
|||
p.close();
|
||||
|
||||
const data = new Uint8Array(10);
|
||||
const r = await p.stderr!.read(data);
|
||||
const r = await p.stderr.read(data);
|
||||
assertEquals(r, null);
|
||||
p.stderr!.close();
|
||||
p.stderr.close();
|
||||
});
|
||||
|
||||
unitTest(function signalNumbers(): void {
|
||||
|
|
|
@ -7,7 +7,9 @@ import { delay } from "../../async/delay.ts";
|
|||
|
||||
const { test } = Deno;
|
||||
|
||||
async function startServer(): Promise<Deno.Process> {
|
||||
async function startServer(): Promise<
|
||||
Deno.Process<Deno.RunOptions & { stdout: "piped" }>
|
||||
> {
|
||||
const server = Deno.run({
|
||||
// TODO(lucacasonato): remove unstable once possible
|
||||
cmd: [
|
||||
|
@ -27,7 +29,7 @@ async function startServer(): Promise<Deno.Process> {
|
|||
const s = await r.readLine();
|
||||
assert(s !== null && s.includes("chat server starting"));
|
||||
} catch (err) {
|
||||
server.stdout!.close();
|
||||
server.stdout.close();
|
||||
server.close();
|
||||
}
|
||||
|
||||
|
@ -46,7 +48,7 @@ test({
|
|||
assert(html.includes("ws chat example"), "body is ok");
|
||||
} finally {
|
||||
server.close();
|
||||
server.stdout!.close();
|
||||
server.stdout.close();
|
||||
}
|
||||
await delay(10);
|
||||
},
|
||||
|
@ -66,7 +68,7 @@ test({
|
|||
assertEquals((await it.next()).value, "[1]: Hello");
|
||||
} finally {
|
||||
server.close();
|
||||
server.stdout!.close();
|
||||
server.stdout.close();
|
||||
ws!.conn.close();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -17,7 +17,7 @@ Deno.test("[examples/catj] print an array", async () => {
|
|||
|
||||
assertStrictEquals(actual, expected);
|
||||
} finally {
|
||||
process.stdin!.close();
|
||||
process.stdin.close();
|
||||
process.close();
|
||||
}
|
||||
});
|
||||
|
@ -36,7 +36,7 @@ Deno.test("[examples/catj] print an object", async () => {
|
|||
|
||||
assertStrictEquals(actual, expected);
|
||||
} finally {
|
||||
process.stdin!.close();
|
||||
process.stdin.close();
|
||||
process.close();
|
||||
}
|
||||
});
|
||||
|
@ -54,7 +54,7 @@ Deno.test("[examples/catj] print multiple files", async () => {
|
|||
|
||||
assertStrictEquals(actual, expected);
|
||||
} finally {
|
||||
process.stdin!.close();
|
||||
process.stdin.close();
|
||||
process.close();
|
||||
}
|
||||
});
|
||||
|
@ -64,8 +64,8 @@ Deno.test("[examples/catj] read from stdin", async () => {
|
|||
const process = catj("-");
|
||||
const input = `{ "foo": "bar" }`;
|
||||
try {
|
||||
await process.stdin!.write(new TextEncoder().encode(input));
|
||||
process.stdin!.close();
|
||||
await process.stdin.write(new TextEncoder().encode(input));
|
||||
process.stdin.close();
|
||||
const output = await process.output();
|
||||
const actual = decoder.decode(output).trim();
|
||||
|
||||
|
@ -75,7 +75,9 @@ Deno.test("[examples/catj] read from stdin", async () => {
|
|||
}
|
||||
});
|
||||
|
||||
function catj(...files: string[]): Deno.Process {
|
||||
function catj(
|
||||
...files: string[]
|
||||
): Deno.Process<Deno.RunOptions & { stdin: "piped"; stdout: "piped" }> {
|
||||
return Deno.run({
|
||||
cmd: [Deno.execPath(), "run", "--allow-read", "catj.ts", ...files],
|
||||
cwd: "examples",
|
||||
|
|
|
@ -13,7 +13,7 @@ Deno.test("[examples/echo_server]", async () => {
|
|||
|
||||
let conn: Deno.Conn | undefined;
|
||||
try {
|
||||
const processReader = new BufReader(process.stdout!);
|
||||
const processReader = new BufReader(process.stdout);
|
||||
const message = await processReader.readLine();
|
||||
|
||||
assertNotEquals(message, null);
|
||||
|
@ -38,7 +38,7 @@ Deno.test("[examples/echo_server]", async () => {
|
|||
assertStrictEquals(actualResponse, expectedResponse);
|
||||
} finally {
|
||||
conn?.close();
|
||||
process.stdout!.close();
|
||||
process.stdout.close();
|
||||
process.close();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import { TextProtoReader } from "../textproto/mod.ts";
|
|||
import { ServerRequest } from "./server.ts";
|
||||
import { serveFile } from "./file_server.ts";
|
||||
const { test } = Deno;
|
||||
let fileServer: Deno.Process;
|
||||
let fileServer: Deno.Process<Deno.RunOptions & { stdout: "piped" }>;
|
||||
|
||||
type FileServerCfg = {
|
||||
target?: string;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { BufReader, BufWriter } from "../io/bufio.ts";
|
|||
import { TextProtoReader } from "../textproto/mod.ts";
|
||||
const { connect, run, test } = Deno;
|
||||
|
||||
let server: Deno.Process;
|
||||
let server: Deno.Process<Deno.RunOptions & { stdout: "piped" }>;
|
||||
async function startServer(): Promise<void> {
|
||||
server = run({
|
||||
// TODO(lucacasonato): remove unstable when stabilized
|
||||
|
@ -18,7 +18,7 @@ async function startServer(): Promise<void> {
|
|||
}
|
||||
function killServer(): void {
|
||||
server.close();
|
||||
server.stdout?.close();
|
||||
server.stdout.close();
|
||||
}
|
||||
|
||||
const input = [
|
||||
|
|
|
@ -372,7 +372,7 @@ test({
|
|||
.catch((_): void => {}); // Ignores the error when closing the process.
|
||||
|
||||
try {
|
||||
const r = new TextProtoReader(new BufReader(p.stdout!));
|
||||
const r = new TextProtoReader(new BufReader(p.stdout));
|
||||
const s = await r.readLine();
|
||||
assert(s !== null && s.includes("server listening"));
|
||||
await delay(100);
|
||||
|
@ -387,7 +387,7 @@ test({
|
|||
// Stops the sever and allows `p.status()` promise to resolve
|
||||
Deno.kill(p.pid, Deno.Signal.SIGKILL);
|
||||
await statusPromise;
|
||||
p.stdout!.close();
|
||||
p.stdout.close();
|
||||
p.close();
|
||||
}
|
||||
},
|
||||
|
@ -417,7 +417,7 @@ test({
|
|||
.catch((_): void => {}); // Ignores the error when closing the process.
|
||||
|
||||
try {
|
||||
const r = new TextProtoReader(new BufReader(p.stdout!));
|
||||
const r = new TextProtoReader(new BufReader(p.stdout));
|
||||
const s = await r.readLine();
|
||||
assert(
|
||||
s !== null && s.includes("server listening"),
|
||||
|
@ -444,7 +444,7 @@ test({
|
|||
// Stops the sever and allows `p.status()` promise to resolve
|
||||
Deno.kill(p.pid, Deno.Signal.SIGKILL);
|
||||
await statusPromise;
|
||||
p.stdout!.close();
|
||||
p.stdout.close();
|
||||
p.close();
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue