diff --git a/src/runtime/process.ts b/src/runtime/process.ts index 899880c..813419c 100644 --- a/src/runtime/process.ts +++ b/src/runtime/process.ts @@ -21,6 +21,8 @@ export class Process implements Promise { #baseError: ProcessError; #maxRetries = 0; #retries = 0; + #timeout = 0; + #timer = 0; #throwErrors = true; #isKilled = false; @@ -45,6 +47,10 @@ export class Process implements Promise { stdout: this.#stdout, stderr: this.#stderr, }); + + if (this.#timeout) { + this.#timer = setTimeout(() => this.kill("SIGABRT"), this.#timeout); + } } return this.#proc; } @@ -67,6 +73,11 @@ export class Process implements Promise { return this; } + timeout(timeout: number): this { + this.#timeout = timeout; + return this; + } + kill(signo: Deno.Signal): void { this.#isKilled = true; this.#process.kill(signo); @@ -154,6 +165,7 @@ export class Process implements Promise { } } this.#close(); + this.#closeTimer(); return output; } catch (error) { @@ -165,6 +177,7 @@ export class Process implements Promise { return this.#run(); } + this.#closeTimer(); throw error; } @@ -176,6 +189,10 @@ export class Process implements Promise { this.#proc?.stdout?.close(); this.#proc?.stderr?.close(); } + + #closeTimer() { + this.#timer && clearTimeout(this.#timer); + } } async function read( diff --git a/test.ts b/test.ts index 98d62a8..d7ef3b3 100644 --- a/test.ts +++ b/test.ts @@ -119,6 +119,22 @@ Deno.test("$ should not throw with noThrow", async () => { assertEquals(result.status.code, 1); }); +Deno.test("$ should throw on timeout (zsh)", async () => { + $.shell = "/bin/zsh"; + await assertRejects( + () => $`sleep 10`.timeout(100), + ProcessError, + ); + $.shell = "/bin/bash"; +}); + +Deno.test("$ should not throw if timeout is not reached (zsh)", async () => { + $.shell = "/bin/zsh"; + const { status } = await $`sleep 0.1`.timeout(10000); + assertEquals(status.code, 0); + $.shell = "/bin/bash"; +}); + Deno.test({ name: "$ should kill the process (bash)", async fn() {