@sdxc/cli 2026.10.20 → 2026.10.23

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/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import { failure, isFailure, success } from "@sdxc/result";
10
10
  import { CommandContext } from "./context.js";
11
11
  import { describeEntry } from "./describe.js";
12
12
  import { CancelledError, EXIT_CODES, exitCodeOf, UsageError } from "./errors.js";
13
+ import { ExecError } from "./exec.js";
13
14
  import { isAction } from "./handler.js";
14
15
  import { renderHelp } from "./help.js";
15
16
  import { isLazy } from "./lazy.js";
@@ -216,7 +217,10 @@ async function dispatch(chain, context, handler) {
216
217
  function asError(error) {
217
218
  return error instanceof Error ? error : new Error(String(error));
218
219
  }
219
- /** Prints a failed run's message and answers its exit code. */
220
+ /**
221
+ * Prints a failed run's message and answers its exit code. An `ExecError` also prints the
222
+ * child's stderr so CI sees wrangler/cf authentication lines instead of only "exited with 1".
223
+ */
220
224
  function report(error, entry, session, program) {
221
225
  if (error instanceof UsageError) {
222
226
  let help = renderHelp(entry, program, session.style.stderr);
@@ -224,6 +228,9 @@ function report(error, entry, session, program) {
224
228
  }
225
229
  else if (!(error instanceof CancelledError) && error.message !== "") {
226
230
  session.screen.write(session.stderr, `${error.message}\n`);
231
+ if (error instanceof ExecError && error.stderr !== "") {
232
+ session.screen.write(session.stderr, error.stderr.endsWith("\n") ? error.stderr : `${error.stderr}\n`);
233
+ }
227
234
  }
228
235
  return exitCodeOf(error);
229
236
  }
package/dist/exec.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * @copyright Sergio Xalambrí 2026
8
8
  */
9
9
  import { spawn } from "node:child_process";
10
+ import { iife } from "@sdxc/iife";
10
11
  import { failure, success } from "@sdxc/result";
11
12
  import { CancelledError } from "./errors.js";
12
13
  /** A child that could not start or exited with a code other than 0. */
@@ -61,27 +62,33 @@ export const spawnExec = (command, request) => new Promise((resolve) => {
61
62
  }
62
63
  let shown = displayCommand(command);
63
64
  let direct = (target, stream) => target === stream;
64
- let outTarget = request.stdio === "inherit"
65
- ? request.stdout
66
- : request.stdio === "stderr"
67
- ? request.stderr
68
- : null;
65
+ let outTarget = null;
66
+ if (request.stdio === "inherit")
67
+ outTarget = request.stdout;
68
+ else if (request.stdio === "stderr")
69
+ outTarget = request.stderr;
69
70
  let errTarget = request.stdio === "inherit" || request.stdio === "stderr" ? request.stderr : null;
70
- let stdout = request.stdio === "ignore"
71
- ? "ignore"
72
- : outTarget === null
73
- ? "pipe"
74
- : direct(outTarget, process.stdout)
75
- ? "inherit"
76
- : direct(outTarget, process.stderr)
77
- ? 2
78
- : "pipe";
79
- let stderr = request.stdio === "ignore"
80
- ? "ignore"
81
- : errTarget !== null && direct(errTarget, process.stderr)
82
- ? "inherit"
83
- : "pipe";
84
- let stdin = request.input !== undefined ? "pipe" : request.stdio === "inherit" ? "inherit" : "ignore";
71
+ let stdout = iife(() => {
72
+ if (request.stdio === "ignore")
73
+ return "ignore";
74
+ if (outTarget === null)
75
+ return "pipe";
76
+ if (direct(outTarget, process.stdout))
77
+ return "inherit";
78
+ if (direct(outTarget, process.stderr))
79
+ return 2;
80
+ return "pipe";
81
+ });
82
+ let stderr = "pipe";
83
+ if (request.stdio === "ignore")
84
+ stderr = "ignore";
85
+ else if (errTarget !== null && direct(errTarget, process.stderr))
86
+ stderr = "inherit";
87
+ let stdin = "ignore";
88
+ if (request.input !== undefined)
89
+ stdin = "pipe";
90
+ else if (request.stdio === "inherit")
91
+ stdin = "inherit";
85
92
  let child = spawn(program, args, {
86
93
  cwd: request.cwd,
87
94
  env: { ...request.env },
package/dist/help.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * @author [Sergio Xalambrí](https://sergiodxa.com)
7
7
  * @copyright Sergio Xalambrí 2026
8
8
  */
9
+ import { iife } from "@sdxc/iife";
9
10
  import { groupSpec, isGroup } from "./command.js";
10
11
  import { visibleWidth } from "./style.js";
11
12
  /** Renders `entry`'s help, ending with a newline. */
@@ -99,11 +100,13 @@ function flagLabel(spec) {
99
100
  if (spec.short !== undefined)
100
101
  names.push(`-${spec.short}`);
101
102
  if (spec.kind === "boolean") {
102
- let prefix = spec.prompt !== undefined || spec.default === undefined
103
- ? "[no-]"
104
- : spec.default === true
105
- ? "no-"
106
- : "";
103
+ let prefix = iife(() => {
104
+ if (spec.prompt !== undefined || spec.default === undefined)
105
+ return "[no-]";
106
+ if (spec.default === true)
107
+ return "no-";
108
+ return "";
109
+ });
107
110
  names.push(`--${prefix}${spec.name}`);
108
111
  for (let alias of spec.aliases)
109
112
  names.push(`--${prefix}${alias}`);
@@ -6,6 +6,7 @@
6
6
  * @author [Sergio Xalambrí](https://sergiodxa.com)
7
7
  * @copyright Sergio Xalambrí 2026
8
8
  */
9
+ import { iife } from "@sdxc/iife";
9
10
  import { typed } from "./keys.js";
10
11
  import { normalizeChoices } from "./prompt.js";
11
12
  import { graphemes } from "./style.js";
@@ -150,9 +151,11 @@ function numberLine(options, initial) {
150
151
  let current = editor.text === "" ? (fallback ?? options.min ?? 0) : Number(editor.text);
151
152
  if (Number.isNaN(current))
152
153
  current = fallback ?? 0;
153
- let next = editor.text === "" && fallback === undefined
154
- ? current
155
- : current + (key.name === "up" ? step : -step);
154
+ let next = iife(() => {
155
+ if (editor.text === "" && fallback === undefined)
156
+ return current;
157
+ return current + (key.name === "up" ? step : -step);
158
+ });
156
159
  editor = new Editor(String(clamp(next)));
157
160
  return undefined;
158
161
  }
@@ -261,11 +264,13 @@ function list(choices, initial, mode) {
261
264
  render(style, header) {
262
265
  let lines = [];
263
266
  if (mode.search === true) {
264
- let shownQuery = query.chars.length === 0
265
- ? mode.placeholder === undefined
266
- ? style.inverse(" ")
267
- : style.dim(mode.placeholder)
268
- : query.render(style);
267
+ let shownQuery = iife(() => {
268
+ if (query.chars.length === 0)
269
+ return mode.placeholder === undefined
270
+ ? style.inverse(" ")
271
+ : style.dim(mode.placeholder);
272
+ return query.render(style);
273
+ });
269
274
  lines.push(`${header} › ${shownQuery}`);
270
275
  }
271
276
  else
@@ -283,12 +288,18 @@ function list(choices, initial, mode) {
283
288
  for (let index of window) {
284
289
  let option = options[index];
285
290
  let pointer = index === cursor ? style.cyan("❯") : " ";
286
- let box = mode.mode === "many" ? `${selected.has(index) ? style.green("◉") : "◯"} ` : "";
287
- let label = option.disabled
288
- ? style.dim(`${option.label} (disabled)`)
289
- : index === cursor
290
- ? style.cyan(option.label)
291
- : option.label;
291
+ let box;
292
+ if (mode.mode === "many")
293
+ box = `${selected.has(index) ? style.green("◉") : "◯"} `;
294
+ else
295
+ box = "";
296
+ let label;
297
+ if (option.disabled)
298
+ label = style.dim(`${option.label} (disabled)`);
299
+ else if (index === cursor)
300
+ label = style.cyan(option.label);
301
+ else
302
+ label = option.label;
292
303
  let hint = option.hint === "" ? "" : ` ${style.dim(option.hint)}`;
293
304
  lines.push(`${pointer} ${box}${label}${hint}`);
294
305
  }
package/dist/keys.js CHANGED
@@ -7,6 +7,7 @@
7
7
  * @copyright Sergio Xalambrí 2026
8
8
  */
9
9
  import { emitKeypressEvents } from "node:readline";
10
+ import { iife } from "@sdxc/iife";
10
11
  import { graphemes } from "./style.js";
11
12
  /**
12
13
  * Starts reading keys from `input` in raw mode.
@@ -101,7 +102,13 @@ export function keyFromName(name) {
101
102
  let bare = ctrl ? name.slice(5) : name;
102
103
  let named = NAMED_KEYS[bare];
103
104
  if (named !== undefined) {
104
- let sequence = named === "space" ? " " : named === "return" ? "\r" : "";
105
+ let sequence = iife(() => {
106
+ if (named === "space")
107
+ return " ";
108
+ if (named === "return")
109
+ return "\r";
110
+ return "";
111
+ });
105
112
  return { name: named, sequence, ctrl, meta: false, shift: false };
106
113
  }
107
114
  let shift = bare !== bare.toLowerCase();
package/dist/parse.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * @author [Sergio Xalambrí](https://sergiodxa.com)
7
7
  * @copyright Sergio Xalambrí 2026
8
8
  */
9
+ import { iife } from "@sdxc/iife";
9
10
  import { isGroup } from "./command.js";
10
11
  import { closest } from "./suggest.js";
11
12
  /** A token that starts a flag: a dash and something after it, other than a negative number. */
@@ -203,11 +204,14 @@ function unknownFlag(tree, entry, token) {
203
204
  let owners = long ? (tree.owners.get(name) ?? tree.owners.get(positive) ?? []) : [];
204
205
  if (owners.length > 0) {
205
206
  let titles = owners.map((owner) => owner.title);
206
- let listed = titles.length === 1
207
- ? titles[0]
208
- : titles.length > NAMED_OWNERS
209
- ? `${titles.slice(0, NAMED_OWNERS).join(", ")} and ${titles.length - NAMED_OWNERS} more`
210
- : `${titles.slice(0, -1).join(", ")} and ${titles.at(-1)}`;
207
+ let listed = iife(() => {
208
+ if (titles.length === 1)
209
+ return titles[0];
210
+ if (titles.length > NAMED_OWNERS) {
211
+ return `${titles.slice(0, NAMED_OWNERS).join(", ")} and ${titles.length - NAMED_OWNERS} more`;
212
+ }
213
+ return `${titles.slice(0, -1).join(", ")} and ${titles.at(-1)}`;
214
+ });
211
215
  return `${spelled} is not a flag of ${entry.title}; ${listed} ${titles.length === 1 ? "takes" : "take"} it`;
212
216
  }
213
217
  let candidates = [];
package/dist/resolve.js CHANGED
@@ -6,6 +6,7 @@
6
6
  * @author [Sergio Xalambrí](https://sergiodxa.com)
7
7
  * @copyright Sergio Xalambrí 2026
8
8
  */
9
+ import { iife } from "@sdxc/iife";
9
10
  import { failure, isFailure, success } from "@sdxc/result";
10
11
  import { isGroup } from "./command.js";
11
12
  import { CancelledError, UsageError } from "./errors.js";
@@ -109,13 +110,14 @@ export async function resolveInputs(parsed, session) {
109
110
  return success(spec.variadic ? checked.data : checked.data[0]);
110
111
  };
111
112
  if (given.length === 0) {
112
- let fallback = spec.hasDefault
113
- ? { value: spec.default }
114
- : spec.optional
115
- ? { value: undefined }
116
- : spec.variadic && spec.min === 0
117
- ? { value: [] }
118
- : undefined;
113
+ let fallback = iife(() => {
114
+ if (spec.hasDefault)
115
+ return { value: spec.default };
116
+ if (spec.optional)
117
+ return { value: undefined };
118
+ if (spec.variadic && spec.min === 0)
119
+ return { value: [] };
120
+ });
119
121
  missing.push({
120
122
  label,
121
123
  names: [key],
@@ -154,13 +156,14 @@ export async function resolveInputs(parsed, session) {
154
156
  return success(many ? checked.data : checked.data[0]);
155
157
  };
156
158
  if (occurrences.length === 0) {
157
- let fallback = spec.hasDefault
158
- ? { value: spec.default }
159
- : spec.kind === "boolean"
160
- ? { value: false }
161
- : spec.required
162
- ? undefined
163
- : { value: many ? [] : undefined };
159
+ let fallback = iife(() => {
160
+ if (spec.hasDefault)
161
+ return { value: spec.default };
162
+ if (spec.kind === "boolean")
163
+ return { value: false };
164
+ if (!spec.required)
165
+ return { value: many ? [] : undefined };
166
+ });
164
167
  missing.push({
165
168
  label,
166
169
  names: [key, spec.name],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdxc/cli",
3
- "version": "2026.10.20",
3
+ "version": "2026.10.23",
4
4
  "description": "Build command-line tools with generated help, prompts bound to flags, tasks and a test harness",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -9,6 +9,7 @@
9
9
  "./test": "./dist/test.js"
10
10
  },
11
11
  "dependencies": {
12
+ "@sdxc/iife": "2026.10.23",
12
13
  "@sdxc/result": "2026.10.13",
13
14
  "@standard-schema/spec": "^1.1.0"
14
15
  },
@@ -20,7 +21,7 @@
20
21
  "optional": true
21
22
  }
22
23
  },
23
- "gitHead": "bd4473246f405c43055d2fc5e08c62ba35197258",
24
+ "gitHead": "8e428ddf15290d44500a9618ec4d04bd7629fa99",
24
25
  "publishConfig": {
25
26
  "access": "public"
26
27
  },