omkit 0.1.4 → 0.1.5

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.
@@ -1,5 +1,10 @@
1
1
  /** Tunes an action built with {@link command}. */
2
2
  export interface CommandOptions {
3
+ /**
4
+ * Launch `cmdOrFile` directly with these arguments instead of executing it
5
+ * through a shell. Omit to run the command as a shell command.
6
+ */
7
+ args?: readonly string[];
3
8
  /** Directory to run in; relative paths resolve against `process.cwd()`. Default ".". */
4
9
  cwd?: string;
5
10
  /** Full environment for the child (handed as-is, not merged). Omit to inherit. */
@@ -8,9 +13,11 @@ export interface CommandOptions {
8
13
  inheritDebugger?: boolean;
9
14
  }
10
15
  /**
11
- * Runs an executable directly (without a shell) as an action instance.
16
+ * Runs a command as an action instance.
12
17
  *
13
- * Output streams into the run's log under `name`; a non-zero exit fails the
14
- * action; teardown force-kills the process tree.
18
+ * By default the command is executed through the shell.
19
+ *
20
+ * If `options.args` is provided, `cmdOrFile` is treated as an executable and
21
+ * launched directly without a shell.
15
22
  */
16
- export declare function command(name: string, file: string, args?: readonly string[], options?: CommandOptions): import("../index.ts").ActionInstance<void, import("../core/types.ts").NoEvents, void>;
23
+ export declare function command(name: string, cmdOrFile: string, options?: CommandOptions): import("../index.ts").ActionInstance<import("zx").ProcessOutput, import("../core/types.ts").NoEvents, void> | import("../index.ts").ActionInstance<void, import("../core/types.ts").NoEvents, void>;
@@ -1,43 +1,72 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import path from "node:path";
3
3
  import { action } from "../core/action.js";
4
- import { killTree, streamLines } from "../system/proc.js";
4
+ import { debuggerFreeEnv, killTree, streamLines } from "../system/proc.js";
5
5
  /**
6
- * Runs an executable directly (without a shell) as an action instance.
6
+ * Build a zx tagged template that runs `cmd` verbatim.
7
+ */
8
+ const verbatim = (cmd) => Object.assign([cmd], { raw: [cmd] });
9
+ /**
10
+ * Runs a command as an action instance.
7
11
  *
8
- * Output streams into the run's log under `name`; a non-zero exit fails the
9
- * action; teardown force-kills the process tree.
12
+ * By default the command is executed through the shell.
13
+ *
14
+ * If `options.args` is provided, `cmdOrFile` is treated as an executable and
15
+ * launched directly without a shell.
10
16
  */
11
- export function command(name, file, args = [], options = {}) {
17
+ export function command(name, cmdOrFile, options = {}) {
18
+ const cwd = path.resolve(options.cwd ?? ".");
19
+ // Shell command.
20
+ if (options.args === undefined) {
21
+ return action(name).run((ctx) => ctx.proc(name, {
22
+ cwd,
23
+ ...(options.env && { env: options.env }),
24
+ ...(options.inheritDebugger && {
25
+ inheritDebugger: true,
26
+ }),
27
+ })(verbatim(cmdOrFile)))();
28
+ }
29
+ // Direct executable.
12
30
  return action(name).run(async (ctx) => {
13
- const cwd = path.resolve(options.cwd ?? ".");
14
- const child = spawn(file, args, {
31
+ const inherit = options.inheritDebugger ?? Boolean(process.env.OMKIT_INHERIT_DEBUGGER);
32
+ const env = inherit
33
+ ? (options.env ?? process.env)
34
+ : debuggerFreeEnv(options.env ?? process.env);
35
+ const args = options.args ?? [];
36
+ const child = spawn(cmdOrFile, args, {
15
37
  cwd,
16
- env: options.env,
38
+ env,
17
39
  detached: process.platform !== "win32",
18
40
  windowsHide: true,
19
41
  stdio: ["ignore", "pipe", "pipe"],
20
42
  });
21
- console.log(`start · '${file}' ${args.join(" ")} · in ${cwd}`);
22
- void streamLines(child.stdout, (line) => console.info(line));
23
- void streamLines(child.stderr, (line) => console.error(line));
43
+ console.log(`${name} · start · '${cmdOrFile}' ${args.join(" ")} · in ${cwd}`);
44
+ void streamLines(child.stdout, console.info);
45
+ void streamLines(child.stderr, console.error);
24
46
  let finished = false;
25
47
  const onAbort = () => {
26
48
  if (!finished && child.pid !== undefined) {
27
49
  killTree(child.pid);
28
50
  }
29
51
  };
30
- ctx.signal.addEventListener("abort", onAbort, { once: true });
52
+ if (ctx.signal.aborted) {
53
+ onAbort();
54
+ }
55
+ else {
56
+ ctx.signal.addEventListener("abort", onAbort, { once: true });
57
+ }
31
58
  try {
32
59
  await new Promise((resolve, reject) => {
33
- child.once("error", reject);
60
+ child.once("error", (err) => {
61
+ reject(new Error(`${name}: ${err.message}`, { cause: err }));
62
+ });
34
63
  child.once("exit", (code, signal) => {
35
64
  finished = true;
36
- if (code === 0) {
65
+ if (ctx.signal.aborted || code === 0) {
37
66
  resolve();
38
67
  }
39
68
  else {
40
- reject(new Error(signal ? `${name} terminated by ${signal}` : `${name} exited with code ${code}`));
69
+ reject(Object.assign(new Error(signal ? `${name} terminated by ${signal}` : `${name} exited with code ${code}`), { exitCode: code ?? undefined }));
41
70
  }
42
71
  });
43
72
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omkit",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A tiny runtime for developer workflows: typed actions that pass runtime capabilities and emit typed events, under one run and one shared log. The operational/runtime layer to tskb's knowledge layer.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",