@sdxc/cli 2026.10.20 → 2026.10.24
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 +8 -1
- package/dist/exec.js +27 -20
- package/dist/help.js +8 -5
- package/dist/interactions.js +25 -14
- package/dist/keys.js +8 -1
- package/dist/parse.js +9 -5
- package/dist/resolve.js +17 -14
- package/package.json +4 -3
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
|
-
/**
|
|
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 =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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 =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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 =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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}`);
|
package/dist/interactions.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 { 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 =
|
|
154
|
-
|
|
155
|
-
|
|
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 =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
|
287
|
-
|
|
288
|
-
? style.
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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 =
|
|
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 =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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 =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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 =
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
:
|
|
162
|
-
|
|
163
|
-
|
|
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.
|
|
3
|
+
"version": "2026.10.24",
|
|
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,18 +9,19 @@
|
|
|
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
|
},
|
|
15
16
|
"peerDependencies": {
|
|
16
|
-
"remix": "3.
|
|
17
|
+
"remix": "3.1.0"
|
|
17
18
|
},
|
|
18
19
|
"peerDependenciesMeta": {
|
|
19
20
|
"remix": {
|
|
20
21
|
"optional": true
|
|
21
22
|
}
|
|
22
23
|
},
|
|
23
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "f9db0f8f1f51a256e74b94b0f4b1db4be9880370",
|
|
24
25
|
"publishConfig": {
|
|
25
26
|
"access": "public"
|
|
26
27
|
},
|