@lde/task-runner-native 0.1.0 → 0.2.1

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 CHANGED
@@ -1,11 +1,39 @@
1
- # task-runner-native
1
+ # Task Runner Native
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ Run shell commands natively on the host system using Node.js `child_process`.
4
4
 
5
- ## Building
5
+ ## Usage
6
6
 
7
- Run `nx build task-runner-native` to build the library.
7
+ ```typescript
8
+ import { NativeTaskRunner } from '@lde/task-runner-native';
8
9
 
9
- ## Running unit tests
10
+ const runner = new NativeTaskRunner({
11
+ cwd: '/path/to/working/dir', // Optional working directory
12
+ gracefulShutdownTimeout: 5000, // Optional timeout before SIGKILL (default: 5000ms)
13
+ });
10
14
 
11
- Run `nx test task-runner-native` to execute the unit tests via [Jest](https://jestjs.io).
15
+ // Run a command
16
+ const task = await runner.run('echo "Hello World"');
17
+
18
+ // Wait for completion
19
+ const output = await runner.wait(task);
20
+ console.log(output); // "Hello World"
21
+
22
+ // Or stop a long-running task
23
+ const task2 = await runner.run('sleep 60');
24
+ await runner.stop(task2); // Sends SIGTERM, then SIGKILL after timeout
25
+ ```
26
+
27
+ ## Options
28
+
29
+ | Option | Type | Default | Description |
30
+ | ------------------------- | -------- | ----------------- | --------------------------------------------------------- |
31
+ | `cwd` | `string` | Current directory | Working directory for spawned processes |
32
+ | `gracefulShutdownTimeout` | `number` | `5000` | Milliseconds to wait after SIGTERM before sending SIGKILL |
33
+
34
+ ## Features
35
+
36
+ - Spawns commands in a detached process group
37
+ - Graceful shutdown with SIGTERM → SIGKILL escalation
38
+ - Handles already-exited processes in `stop()`
39
+ - Captures stdout and stderr output
package/dist/index.d.ts CHANGED
@@ -1,9 +1,25 @@
1
1
  import { TaskRunner } from '@lde/task-runner';
2
2
  import { ChildProcess } from 'node:child_process';
3
+ export interface NativeTaskRunnerOptions {
4
+ /**
5
+ * Working directory for spawned processes.
6
+ * Defaults to the current working directory.
7
+ */
8
+ cwd?: string;
9
+ /**
10
+ * Timeout in milliseconds to wait for graceful shutdown (SIGTERM)
11
+ * before escalating to SIGKILL.
12
+ * @default 5000
13
+ */
14
+ gracefulShutdownTimeout?: number;
15
+ }
3
16
  export declare class NativeTaskRunner implements TaskRunner<ChildProcess> {
4
17
  private stdout;
5
18
  private stderr;
6
19
  private shell;
20
+ private cwd?;
21
+ private gracefulShutdownTimeout;
22
+ constructor(options?: NativeTaskRunnerOptions);
7
23
  run(command: string): Promise<ChildProcess>;
8
24
  wait(task: ChildProcess): Promise<string>;
9
25
  stop(task: ChildProcess): Promise<string | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAGzD,qBAAa,gBAAiB,YAAW,UAAU,CAAC,YAAY,CAAC;IAC/D,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,KAAK,CAAQ;IAEf,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsC3C,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzC,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAWtD,OAAO,CAAC,UAAU;CAQnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAS,MAAM,oBAAoB,CAAC;AAGzD,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,qBAAa,gBAAiB,YAAW,UAAU,CAAC,YAAY,CAAC;IAC/D,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,GAAG,CAAC,CAAS;IACrB,OAAO,CAAC,uBAAuB,CAAS;gBAE5B,OAAO,CAAC,EAAE,uBAAuB;IAKvC,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAsC3C,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzC,IAAI,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwCtD,OAAO,CAAC,UAAU;CAQnB"}
package/dist/index.js CHANGED
@@ -4,11 +4,17 @@ export class NativeTaskRunner {
4
4
  stdout = new Map();
5
5
  stderr = new Map();
6
6
  shell = true;
7
+ cwd;
8
+ gracefulShutdownTimeout;
9
+ constructor(options) {
10
+ this.cwd = options?.cwd;
11
+ this.gracefulShutdownTimeout = options?.gracefulShutdownTimeout ?? 5000;
12
+ }
7
13
  async run(command) {
8
14
  const task = spawn(command, {
9
15
  detached: true,
10
16
  shell: this.shell,
11
- cwd: 'imports', // TODO: don't hard-code
17
+ cwd: this.cwd,
12
18
  });
13
19
  task.on('close', (code) => {
14
20
  /** code is null when the process was killed, which is expected when
@@ -49,12 +55,38 @@ export class NativeTaskRunner {
49
55
  }
50
56
  async stop(task) {
51
57
  return new Promise((resolve) => {
52
- task.on('close', () => {
58
+ // Handle already-exited processes.
59
+ if (task.exitCode !== null || task.killed) {
53
60
  resolve(this.taskOutput(task));
54
- });
61
+ return;
62
+ }
63
+ const sigkillTimer = {};
64
+ const cleanup = () => {
65
+ if (sigkillTimer.current) {
66
+ clearTimeout(sigkillTimer.current);
67
+ }
68
+ resolve(this.taskOutput(task));
69
+ };
70
+ task.on('close', cleanup);
55
71
  // Negative PID to kill whole process group: the {shell: true} argument
56
72
  // to spawn splits off a separate process.
57
- process.kill(-task.pid, 'SIGTERM');
73
+ try {
74
+ process.kill(-task.pid, 'SIGTERM');
75
+ }
76
+ catch {
77
+ // Process may have already exited (ESRCH error).
78
+ cleanup();
79
+ return;
80
+ }
81
+ // Escalate to SIGKILL after timeout if process doesn't terminate.
82
+ sigkillTimer.current = setTimeout(() => {
83
+ try {
84
+ process.kill(-task.pid, 'SIGKILL');
85
+ }
86
+ catch {
87
+ // Process may have exited between SIGTERM and SIGKILL.
88
+ }
89
+ }, this.gracefulShutdownTimeout);
58
90
  });
59
91
  }
60
92
  taskOutput(task) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/task-runner-native",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "repository": {
5
5
  "url": "https://github.com/ldengine/lde",
6
6
  "directory": "packages/task-runner-native"
@@ -23,7 +23,7 @@
23
23
  "!**/*.tsbuildinfo"
24
24
  ],
25
25
  "dependencies": {
26
- "@lde/task-runner": "0.1.0",
26
+ "@lde/task-runner": "0.2.1",
27
27
  "tslib": "^2.3.0"
28
28
  }
29
29
  }