From c0b6efc9cbf8fa88398ccc863a8eb554d3a76f93 Mon Sep 17 00:00:00 2001
From: ehmicky <ehmicky@users.noreply.github.com>
Date: Wed, 7 Aug 2024 20:29:07 +0100
Subject: [PATCH] Document how to terminate hanging subprocesses (#1140)

---
 docs/termination.md | 47 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/docs/termination.md b/docs/termination.md
index 8c445cd2e2..99ed31da03 100644
--- a/docs/termination.md
+++ b/docs/termination.md
@@ -124,6 +124,8 @@ If the subprocess is still alive after 5 seconds, it is forcefully terminated wi
 
 ## Timeout
 
+### Execution timeout
+
 If the subprocess lasts longer than the [`timeout`](api.md#optionstimeout) option, a [`SIGTERM` signal](#default-signal) is sent to it.
 
 ```js
@@ -138,6 +140,51 @@ try {
 }
 ```
 
+### Inactivity timeout
+
+To terminate a subprocess when it becomes inactive, the [`cancelSignal`](#canceling) option can be combined with [transforms](transform.md) and some [debouncing logic](https://github.com/sindresorhus/debounce-fn). The following example terminates the subprocess if it has not printed to [`stdout`](api.md#resultstdout)/[`stderr`](api.md#resultstderr) in the last minute.
+
+```js
+import {execa} from 'execa';
+import debounceFn from 'debounce-fn';
+
+// 1 minute
+const wait = 60_000;
+
+const getInactivityOptions = () => {
+	const controller = new AbortController();
+	const cancelSignal = controller.signal;
+
+	// Delay and debounce `cancelSignal` each time `controller.abort()` is called
+	const scheduleAbort = debounceFn(controller.abort.bind(controller), {wait});
+
+	const onOutput = {
+		* transform(data) {
+			// When anything is printed, debounce `controller.abort()`
+			scheduleAbort();
+
+			// Keep the output as is
+			yield data;
+		},
+		// Debounce even if the output does not include any newline
+		binary: true,
+	};
+
+	// Start debouncing
+	scheduleAbort();
+
+	return {
+		cancelSignal,
+		stdout: onOutput,
+		stderr: onOutput,
+	};
+};
+
+const options = getInactivityOptions();
+
+await execa(options)`npm run build`;
+```
+
 ## Current process exit
 
 If the current process exits, the subprocess is automatically [terminated](#default-signal) unless either: