@sdxc/cli 0.0.0-pre.1 → 0.0.0-stage
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 +2 -363
- package/package.json +5 -40
- package/LICENSE.md +0 -21
- package/dist/argument.d.ts +0 -120
- package/dist/argument.js +0 -104
- package/dist/cli.d.ts +0 -68
- package/dist/cli.js +0 -186
- package/dist/command.d.ts +0 -139
- package/dist/command.js +0 -79
- package/dist/context.d.ts +0 -138
- package/dist/context.js +0 -175
- package/dist/errors.d.ts +0 -67
- package/dist/errors.js +0 -82
- package/dist/exec.d.ts +0 -83
- package/dist/exec.js +0 -136
- package/dist/flag.d.ts +0 -175
- package/dist/flag.js +0 -91
- package/dist/handler.d.ts +0 -73
- package/dist/handler.js +0 -30
- package/dist/help.d.ts +0 -18
- package/dist/help.js +0 -141
- package/dist/index.d.ts +0 -35
- package/dist/index.js +0 -20
- package/dist/interactions.d.ts +0 -27
- package/dist/interactions.js +0 -373
- package/dist/keys.d.ts +0 -49
- package/dist/keys.js +0 -146
- package/dist/lazy.d.ts +0 -29
- package/dist/lazy.js +0 -23
- package/dist/middleware.d.ts +0 -78
- package/dist/middleware.js +0 -9
- package/dist/parse.d.ts +0 -38
- package/dist/parse.js +0 -218
- package/dist/prompt.d.ts +0 -153
- package/dist/prompt.js +0 -53
- package/dist/resolve.d.ts +0 -27
- package/dist/resolve.js +0 -294
- package/dist/schema.d.ts +0 -27
- package/dist/schema.js +0 -34
- package/dist/screen.d.ts +0 -42
- package/dist/screen.js +0 -114
- package/dist/session.d.ts +0 -84
- package/dist/session.js +0 -219
- package/dist/style.d.ts +0 -50
- package/dist/style.js +0 -72
- package/dist/suggest.d.ts +0 -15
- package/dist/suggest.js +0 -50
- package/dist/task.d.ts +0 -47
- package/dist/task.js +0 -213
- package/dist/terminal.d.ts +0 -21
- package/dist/terminal.js +0 -101
- package/dist/test.d.ts +0 -89
- package/dist/test.js +0 -140
- package/dist/tree.d.ts +0 -58
- package/dist/tree.js +0 -97
package/dist/middleware.d.ts
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The middleware chain a CLI runs every command inside, and the types that carry what each
|
|
3
|
-
* middleware publishes into the context. The effect a middleware declares rides along as
|
|
4
|
-
* type-only metadata, so an inline `middleware: [...]` array is enough for a handler to read
|
|
5
|
-
* `ctx.db` with its real type; the shape matches `@sdxc/jobs`, so one package ships both.
|
|
6
|
-
*
|
|
7
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
8
|
-
* @copyright Sergio Xalambrí 2026
|
|
9
|
-
*/
|
|
10
|
-
import type { Result } from "@sdxc/result";
|
|
11
|
-
import type { AnyCommandContext } from "./context.js";
|
|
12
|
-
/** What a command answers: success exits 0, a failure exits by its kind. */
|
|
13
|
-
export type CommandResult = Result<unknown, Error>;
|
|
14
|
-
/**
|
|
15
|
-
* Runs the rest of the chain, ending in the command's handler, and resolves to what it answered,
|
|
16
|
-
* so a middleware can log or wrap the outcome; a throw in the rest of the chain rejects it.
|
|
17
|
-
*/
|
|
18
|
-
export type NextFunction = () => Promise<CommandResult>;
|
|
19
|
-
/** What one middleware puts into the context, and the property it installs it as. */
|
|
20
|
-
export interface ContextEffect {
|
|
21
|
-
key: object;
|
|
22
|
-
value: unknown;
|
|
23
|
-
property?: string;
|
|
24
|
-
}
|
|
25
|
-
/** A middleware that publishes nothing a handler can read off the context. */
|
|
26
|
-
export interface EmptyContextEffect {
|
|
27
|
-
key: never;
|
|
28
|
-
value: never;
|
|
29
|
-
}
|
|
30
|
-
/** Type-only slot carrying a middleware's declared effect. */
|
|
31
|
-
declare const contextEffect: unique symbol;
|
|
32
|
-
/**
|
|
33
|
-
* Runs before the command's handler, in the order declared. `Effect` names what it publishes; a
|
|
34
|
-
* middleware publishing several values declares a union of effects, one per property. It calls `next()` to continue; one
|
|
35
|
-
* that returns a failure instead ends the run with it, the way a handler's failure does, and one
|
|
36
|
-
* that returns without calling `next()` ends the run successfully without the handler.
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* function project(): CommandMiddleware<{ key: typeof Project; value: Root; property: "project" }> {
|
|
40
|
-
* return async (ctx, next) => {
|
|
41
|
-
* let root = await findProjectRoot(ctx.cwd);
|
|
42
|
-
* if (isFailure(root)) return root;
|
|
43
|
-
* ctx.set(Project, root.data, { property: "project" });
|
|
44
|
-
* await next();
|
|
45
|
-
* };
|
|
46
|
-
* }
|
|
47
|
-
* @example CommandMiddleware<{ key: typeof Locale; value: string; property: "locale" } | { key: typeof Intl; value: I18n; property: "intl" }>
|
|
48
|
-
*/
|
|
49
|
-
export type CommandMiddleware<Effect extends ContextEffect = EmptyContextEffect> = ((context: AnyCommandContext, next: NextFunction) => void | CommandResult | Promise<void | CommandResult>) & {
|
|
50
|
-
/** Type-only metadata naming what this middleware publishes. */
|
|
51
|
-
readonly [contextEffect]?: Effect | undefined;
|
|
52
|
-
};
|
|
53
|
-
/** A middleware whatever it publishes, for the places that hold a whole chain. */
|
|
54
|
-
export type AnyCommandMiddleware = CommandMiddleware<any>;
|
|
55
|
-
/** The effect one middleware declared, or nothing when it declared none. */
|
|
56
|
-
type EffectOf<Middleware> = Middleware extends {
|
|
57
|
-
readonly [contextEffect]?: (infer Effect) | undefined;
|
|
58
|
-
} ? Effect extends ContextEffect ? Effect : never : never;
|
|
59
|
-
/** Installs nothing a handler can read: an intersection with this changes no type. */
|
|
60
|
-
type NoProperties = Record<never, never>;
|
|
61
|
-
/** The property one effect installs, ignoring an effect that installs none. */
|
|
62
|
-
type EachProperty<Effect> = Effect extends {
|
|
63
|
-
property: infer Property extends string;
|
|
64
|
-
value: infer Value;
|
|
65
|
-
} ? string extends Property ? NoProperties : {
|
|
66
|
-
readonly [Key in Property]: Value;
|
|
67
|
-
} : NoProperties;
|
|
68
|
-
/** Folds a union of property objects into one object holding all of them. */
|
|
69
|
-
type Merge<Union> = (Union extends unknown ? (each: Union) => void : never) extends (all: infer All) => void ? All : never;
|
|
70
|
-
/** Every property an effect installs: one, or each member of a union of effects. */
|
|
71
|
-
type PropertyOf<Effect> = [Effect] extends [never] ? NoProperties : Merge<EachProperty<Effect>>;
|
|
72
|
-
/**
|
|
73
|
-
* Every property a chain installs, folded left to right so a later middleware overriding a name
|
|
74
|
-
* wins. A chain that is a plain array rather than a tuple carries no order, so it contributes
|
|
75
|
-
* nothing.
|
|
76
|
-
*/
|
|
77
|
-
export type ChainProperties<Chain extends readonly AnyCommandMiddleware[]> = number extends Chain["length"] ? NoProperties : Chain extends readonly [infer First, ...infer Rest extends readonly AnyCommandMiddleware[]] ? Omit<PropertyOf<EffectOf<First>>, keyof ChainProperties<Rest>> & ChainProperties<Rest> : NoProperties;
|
|
78
|
-
export {};
|
package/dist/middleware.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The middleware chain a CLI runs every command inside, and the types that carry what each
|
|
3
|
-
* middleware publishes into the context. The effect a middleware declares rides along as
|
|
4
|
-
* type-only metadata, so an inline `middleware: [...]` array is enough for a handler to read
|
|
5
|
-
* `ctx.db` with its real type; the shape matches `@sdxc/jobs`, so one package ships both.
|
|
6
|
-
*
|
|
7
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
8
|
-
* @copyright Sergio Xalambrí 2026
|
|
9
|
-
*/
|
package/dist/parse.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splits an argument vector into the command it names, its positionals and every flag
|
|
3
|
-
* occurrence, against the indexed contract. Every problem is collected rather than stopping at
|
|
4
|
-
* the first, so one usage error lists all of them.
|
|
5
|
-
*
|
|
6
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
-
* @copyright Sergio Xalambrí 2026
|
|
8
|
-
*/
|
|
9
|
-
import type { Entry, Tree } from "./tree.js";
|
|
10
|
-
/** One appearance of a flag on the command line. */
|
|
11
|
-
export interface Occurrence {
|
|
12
|
-
/** The text after `=` or the next token; `true` for a boolean or a bare optional value. */
|
|
13
|
-
value: string | true;
|
|
14
|
-
/** Written as `--no-<name>`. */
|
|
15
|
-
negated: boolean;
|
|
16
|
-
/** The flag as typed, `--allow-net` or `-f`, for error messages. */
|
|
17
|
-
spelled: string;
|
|
18
|
-
}
|
|
19
|
-
/** What one argument vector asks for. */
|
|
20
|
-
export interface Parsed {
|
|
21
|
-
/** The command or group it reaches. */
|
|
22
|
-
entry: Entry;
|
|
23
|
-
help: boolean;
|
|
24
|
-
version: boolean;
|
|
25
|
-
/** `--no-progress`: tasks print without animation. */
|
|
26
|
-
noProgress: boolean;
|
|
27
|
-
positionals: string[];
|
|
28
|
-
/** Occurrences by the flag's key, in command-line order. */
|
|
29
|
-
occurrences: Map<string, Occurrence[]>;
|
|
30
|
-
issues: string[];
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Parses `argv` against `tree`. Combined short flags (`-xvf`) are read as one unknown name.
|
|
34
|
-
*
|
|
35
|
-
* @param withVersion - Whether `--version` is answered, which it is only at the root of a CLI
|
|
36
|
-
* that declares a version
|
|
37
|
-
*/
|
|
38
|
-
export declare function parseArgv(tree: Tree, argv: readonly string[], withVersion: boolean): Parsed;
|
package/dist/parse.js
DELETED
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Splits an argument vector into the command it names, its positionals and every flag
|
|
3
|
-
* occurrence, against the indexed contract. Every problem is collected rather than stopping at
|
|
4
|
-
* the first, so one usage error lists all of them.
|
|
5
|
-
*
|
|
6
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
-
* @copyright Sergio Xalambrí 2026
|
|
8
|
-
*/
|
|
9
|
-
import { isGroup } from "./command.js";
|
|
10
|
-
import { closest } from "./suggest.js";
|
|
11
|
-
/** A token that starts a flag: a dash and something after it, other than a negative number. */
|
|
12
|
-
function isFlagToken(token) {
|
|
13
|
-
return token.startsWith("-") && token !== "-" && !/^-\d/.test(token);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Parses `argv` against `tree`. Combined short flags (`-xvf`) are read as one unknown name.
|
|
17
|
-
*
|
|
18
|
-
* @param withVersion - Whether `--version` is answered, which it is only at the root of a CLI
|
|
19
|
-
* that declares a version
|
|
20
|
-
*/
|
|
21
|
-
export function parseArgv(tree, argv, withVersion) {
|
|
22
|
-
let parsed = {
|
|
23
|
-
entry: tree.root,
|
|
24
|
-
help: false,
|
|
25
|
-
version: false,
|
|
26
|
-
noProgress: false,
|
|
27
|
-
positionals: [],
|
|
28
|
-
occurrences: new Map(),
|
|
29
|
-
issues: [],
|
|
30
|
-
};
|
|
31
|
-
let index = 0;
|
|
32
|
-
let rest = false;
|
|
33
|
-
/** Flag tokens read at a group, replayed against the member the command line goes on to name. */
|
|
34
|
-
let atGroup = [];
|
|
35
|
-
/** How many issues stood before the current group's flags were read. */
|
|
36
|
-
let groupIssues = 0;
|
|
37
|
-
/** Records a global flag, answering whether `token` was one. */
|
|
38
|
-
function global(token) {
|
|
39
|
-
if (token === "--help" || token === "-h")
|
|
40
|
-
parsed.help = true;
|
|
41
|
-
else if (token === "--no-progress")
|
|
42
|
-
parsed.noProgress = true;
|
|
43
|
-
else if (token === "--version" && withVersion && parsed.entry === tree.root) {
|
|
44
|
-
parsed.version = true;
|
|
45
|
-
}
|
|
46
|
-
else
|
|
47
|
-
return false;
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
while (index < argv.length) {
|
|
51
|
-
let token = argv[index];
|
|
52
|
-
index += 1;
|
|
53
|
-
if (rest) {
|
|
54
|
-
parsed.positionals.push(token);
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (token === "--") {
|
|
58
|
-
rest = true;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (isFlagToken(token)) {
|
|
62
|
-
if (global(token))
|
|
63
|
-
continue;
|
|
64
|
-
let start = index - 1;
|
|
65
|
-
index = readFlag(tree, parsed, argv, index, token);
|
|
66
|
-
if (isGroup(parsed.entry.node))
|
|
67
|
-
atGroup.push(...argv.slice(start, index));
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (isGroup(parsed.entry.node) && parsed.positionals.length === 0) {
|
|
71
|
-
let child = parsed.entry.children.get(token);
|
|
72
|
-
if (child === undefined && parsed.entry.arguments.length > 0) {
|
|
73
|
-
parsed.positionals.push(token);
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (child === undefined) {
|
|
77
|
-
let suggestion = closest(token, parsed.entry.children.keys());
|
|
78
|
-
let hint = suggestion === undefined ? "" : `; did you mean "${suggestion}"?`;
|
|
79
|
-
parsed.issues.push(`Unknown command "${token}" for ${parsed.entry.title}${hint}`);
|
|
80
|
-
for (let remaining of argv.slice(index)) {
|
|
81
|
-
if (remaining === "--help" || remaining === "-h")
|
|
82
|
-
parsed.help = true;
|
|
83
|
-
}
|
|
84
|
-
return parsed;
|
|
85
|
-
}
|
|
86
|
-
parsed.entry = child;
|
|
87
|
-
parsed.issues.length = groupIssues;
|
|
88
|
-
replay(tree, parsed, atGroup);
|
|
89
|
-
groupIssues = parsed.issues.length;
|
|
90
|
-
if (child.rest)
|
|
91
|
-
rest = true;
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
parsed.positionals.push(token);
|
|
95
|
-
}
|
|
96
|
-
if (rest &&
|
|
97
|
-
isGroup(parsed.entry.node) &&
|
|
98
|
-
parsed.entry.arguments.length === 0 &&
|
|
99
|
-
parsed.positionals.length > 0) {
|
|
100
|
-
parsed.issues.push(`Unknown command "${parsed.positionals[0]}" for ${parsed.entry.title}`);
|
|
101
|
-
}
|
|
102
|
-
return parsed;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Re-reads the flag tokens written before a member's name against that member, so
|
|
106
|
-
* `prog make --dry-run job` reaches `job`'s `--dry-run` and a group's flag the member lacks is
|
|
107
|
-
* reported as unknown to it. The caller drops what the group's own reading recorded.
|
|
108
|
-
*/
|
|
109
|
-
function replay(tree, parsed, tokens) {
|
|
110
|
-
let replayed = tokens.splice(0);
|
|
111
|
-
parsed.occurrences = new Map();
|
|
112
|
-
let index = 0;
|
|
113
|
-
while (index < replayed.length) {
|
|
114
|
-
let token = replayed[index];
|
|
115
|
-
index = readFlag(tree, parsed, replayed, index + 1, token);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/** Reads one flag token of a command, answering the index after any value it consumed. */
|
|
119
|
-
function readFlag(tree, parsed, argv, index, token) {
|
|
120
|
-
let long = token.startsWith("--");
|
|
121
|
-
let body = token.slice(long ? 2 : 1);
|
|
122
|
-
let separator = body.indexOf("=");
|
|
123
|
-
let name = separator === -1 ? body : body.slice(0, separator);
|
|
124
|
-
let inline = separator === -1 ? undefined : body.slice(separator + 1);
|
|
125
|
-
let spelled = `${long ? "--" : "-"}${name}`;
|
|
126
|
-
let entry = parsed.entry;
|
|
127
|
-
let negated = false;
|
|
128
|
-
let flag = long ? entry.longs.get(name) : entry.shorts.get(name);
|
|
129
|
-
if (flag === undefined && long && name.startsWith("no-")) {
|
|
130
|
-
let positive = entry.longs.get(name.slice(3));
|
|
131
|
-
if (positive?.spec.kind === "boolean") {
|
|
132
|
-
flag = positive;
|
|
133
|
-
negated = true;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
if (flag === undefined) {
|
|
137
|
-
parsed.issues.push(unknownFlag(tree, entry, token));
|
|
138
|
-
return inline === undefined && ownersTakeValue(tree, long ? name : "")
|
|
139
|
-
? skipValue(argv, index)
|
|
140
|
-
: index;
|
|
141
|
-
}
|
|
142
|
-
let list = parsed.occurrences.get(flag.key) ?? [];
|
|
143
|
-
parsed.occurrences.set(flag.key, list);
|
|
144
|
-
if (flag.spec.kind === "boolean") {
|
|
145
|
-
if (inline !== undefined)
|
|
146
|
-
parsed.issues.push(`${spelled} takes no value`);
|
|
147
|
-
else
|
|
148
|
-
list.push({ value: true, negated, spelled });
|
|
149
|
-
return index;
|
|
150
|
-
}
|
|
151
|
-
if (inline === "") {
|
|
152
|
-
parsed.issues.push(`${spelled} needs a value`);
|
|
153
|
-
return index;
|
|
154
|
-
}
|
|
155
|
-
if (inline !== undefined) {
|
|
156
|
-
list.push({ value: inline, negated: false, spelled });
|
|
157
|
-
return index;
|
|
158
|
-
}
|
|
159
|
-
if (flag.spec.optionalValue) {
|
|
160
|
-
list.push({ value: true, negated: false, spelled });
|
|
161
|
-
return index;
|
|
162
|
-
}
|
|
163
|
-
let next = argv[index];
|
|
164
|
-
if (next === undefined || isFlagToken(next) || next === "--") {
|
|
165
|
-
parsed.issues.push(`${spelled} needs a value`);
|
|
166
|
-
return index;
|
|
167
|
-
}
|
|
168
|
-
list.push({ value: next, negated: false, spelled });
|
|
169
|
-
return index + 1;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Whether every command declaring `--<name>` reads a value after it, so the word following a
|
|
173
|
-
* misplaced `--cron "0 8 * * *"` is its value rather than a second, unexpected argument.
|
|
174
|
-
*/
|
|
175
|
-
function ownersTakeValue(tree, name) {
|
|
176
|
-
let owners = tree.owners.get(name) ?? [];
|
|
177
|
-
return (owners.length > 0 &&
|
|
178
|
-
owners.every((owner) => {
|
|
179
|
-
let spec = owner.longs.get(name)?.spec;
|
|
180
|
-
return spec !== undefined && spec.kind !== "boolean" && !spec.optionalValue;
|
|
181
|
-
}));
|
|
182
|
-
}
|
|
183
|
-
/** The index after the value at `index`, or `index` when the next token is no value. */
|
|
184
|
-
function skipValue(argv, index) {
|
|
185
|
-
let next = argv[index];
|
|
186
|
-
return next === undefined || isFlagToken(next) || next === "--" ? index : index + 1;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* The message for a flag `entry` does not declare: the commands that do declare it, or the
|
|
190
|
-
* closest name it does declare.
|
|
191
|
-
*/
|
|
192
|
-
function unknownFlag(tree, entry, token) {
|
|
193
|
-
let long = token.startsWith("--");
|
|
194
|
-
let body = token.slice(long ? 2 : 1);
|
|
195
|
-
let name = body.split("=")[0];
|
|
196
|
-
let spelled = `${long ? "--" : "-"}${name}`;
|
|
197
|
-
let positive = name.startsWith("no-") ? name.slice(3) : name;
|
|
198
|
-
let owners = long ? (tree.owners.get(name) ?? tree.owners.get(positive) ?? []) : [];
|
|
199
|
-
if (owners.length > 0) {
|
|
200
|
-
let titles = owners.map((owner) => owner.title);
|
|
201
|
-
let listed = titles.length === 1
|
|
202
|
-
? titles[0]
|
|
203
|
-
: `${titles.slice(0, -1).join(", ")} and ${titles.at(-1)}`;
|
|
204
|
-
return `${spelled} is not a flag of ${entry.title}; ${listed} ${titles.length === 1 ? "takes" : "take"} it`;
|
|
205
|
-
}
|
|
206
|
-
let candidates = [];
|
|
207
|
-
if (long) {
|
|
208
|
-
for (let flag of entry.flags) {
|
|
209
|
-
candidates.push(flag.spec.name, ...flag.spec.aliases);
|
|
210
|
-
if (flag.spec.kind === "boolean")
|
|
211
|
-
candidates.push(`no-${flag.spec.name}`);
|
|
212
|
-
}
|
|
213
|
-
candidates.push("help");
|
|
214
|
-
}
|
|
215
|
-
let suggestion = long ? closest(name, candidates) : undefined;
|
|
216
|
-
let hint = suggestion === undefined ? "" : `; did you mean --${suggestion}?`;
|
|
217
|
-
return `Unknown flag ${spelled}${hint}`;
|
|
218
|
-
}
|
package/dist/prompt.d.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The seven prompt kinds as declarations: what a flag or argument asks when the command line
|
|
3
|
-
* leaves it out, and the option shapes `ctx.prompt` takes when a handler asks on its own. A
|
|
4
|
-
* declaration runs nothing; the run decides whether to ask, answer from a default or fail.
|
|
5
|
-
*
|
|
6
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
-
* @copyright Sergio Xalambrí 2026
|
|
8
|
-
*/
|
|
9
|
-
import type { Result } from "@sdxc/result";
|
|
10
|
-
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
11
|
-
import type { CancelledError } from "./errors.js";
|
|
12
|
-
/** Brands a prompt declaration so a flag can tell it from a plain options object. */
|
|
13
|
-
declare const PROMPT: unique symbol;
|
|
14
|
-
/**
|
|
15
|
-
* When a bound prompt asks: `"missing"` only when the command line left its input out and no
|
|
16
|
-
* default applies, `"always"` also when a default exists, preselecting it, for a choice the user
|
|
17
|
-
* should see. A value on the command line skips the prompt either way.
|
|
18
|
-
*/
|
|
19
|
-
export type Ask = "missing" | "always";
|
|
20
|
-
/** One option of a list prompt. */
|
|
21
|
-
export interface Choice<T> {
|
|
22
|
-
value: T;
|
|
23
|
-
/** What the list shows. @default String(value) */
|
|
24
|
-
label?: string;
|
|
25
|
-
/** Dimmed text after the label. */
|
|
26
|
-
hint?: string;
|
|
27
|
-
/** Shown dimmed and skipped by the cursor. */
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
}
|
|
30
|
-
/** A list prompt's options: plain values, shown as written, or choices. */
|
|
31
|
-
export type Choices<T> = readonly (Choice<T> | (T & (string | number)))[];
|
|
32
|
-
/** Options every prompt kind takes. */
|
|
33
|
-
export interface PromptBase {
|
|
34
|
-
/** The question, also printed in the summary line once answered. */
|
|
35
|
-
message: string;
|
|
36
|
-
/** @default "missing" */
|
|
37
|
-
ask?: Ask;
|
|
38
|
-
}
|
|
39
|
-
/** A line of text. */
|
|
40
|
-
export interface TextOptions extends PromptBase {
|
|
41
|
-
/** Dimmed text shown while the answer is empty. */
|
|
42
|
-
placeholder?: string;
|
|
43
|
-
}
|
|
44
|
-
/** A secret line of text. */
|
|
45
|
-
export interface PasswordOptions extends PromptBase {
|
|
46
|
-
/** `false` echoes nothing as the user types. @default true, one `•` per character */
|
|
47
|
-
mask?: boolean;
|
|
48
|
-
}
|
|
49
|
-
/** A number, typed or stepped with the arrow keys. */
|
|
50
|
-
export interface NumberOptions extends PromptBase {
|
|
51
|
-
min?: number;
|
|
52
|
-
max?: number;
|
|
53
|
-
/** How far one arrow press moves. @default 1 */
|
|
54
|
-
step?: number;
|
|
55
|
-
}
|
|
56
|
-
/** A yes-or-no question. */
|
|
57
|
-
export interface ConfirmOptions extends PromptBase {
|
|
58
|
-
/** What the toggle and the summary line call a yes. @default "Yes" */
|
|
59
|
-
active?: string;
|
|
60
|
-
/** What the toggle and the summary line call a no. @default "No" */
|
|
61
|
-
inactive?: string;
|
|
62
|
-
}
|
|
63
|
-
/** One value from a list. */
|
|
64
|
-
export interface SelectOptions<T = unknown> extends PromptBase {
|
|
65
|
-
options: Choices<T>;
|
|
66
|
-
}
|
|
67
|
-
/** Any number of values from a list. */
|
|
68
|
-
export interface MultiselectOptions<T = unknown> extends PromptBase {
|
|
69
|
-
options: Choices<T>;
|
|
70
|
-
/** Fewest values Enter accepts. */
|
|
71
|
-
min?: number;
|
|
72
|
-
/** Most values Enter accepts. */
|
|
73
|
-
max?: number;
|
|
74
|
-
}
|
|
75
|
-
/** One value, or several with `multiple`, from a list filtered by what the user types. */
|
|
76
|
-
export interface SearchOptions<T = unknown> extends PromptBase {
|
|
77
|
-
options: Choices<T>;
|
|
78
|
-
placeholder?: string;
|
|
79
|
-
/** Space toggles options and Enter answers every toggled one. */
|
|
80
|
-
multiple?: boolean;
|
|
81
|
-
min?: number;
|
|
82
|
-
max?: number;
|
|
83
|
-
}
|
|
84
|
-
/** Every prompt kind with the options it takes. */
|
|
85
|
-
export interface PromptKinds {
|
|
86
|
-
text: TextOptions;
|
|
87
|
-
password: PasswordOptions;
|
|
88
|
-
number: NumberOptions;
|
|
89
|
-
confirm: ConfirmOptions;
|
|
90
|
-
select: SelectOptions;
|
|
91
|
-
multiselect: MultiselectOptions;
|
|
92
|
-
search: SearchOptions;
|
|
93
|
-
}
|
|
94
|
-
/** A prompt kind's name. */
|
|
95
|
-
export type PromptKind = keyof PromptKinds;
|
|
96
|
-
/** A prompt bound to the flag or argument it fills. */
|
|
97
|
-
export interface PromptDeclaration<Kind extends PromptKind = PromptKind> {
|
|
98
|
-
readonly [PROMPT]: true;
|
|
99
|
-
readonly kind: Kind;
|
|
100
|
-
readonly options: PromptKinds[Kind];
|
|
101
|
-
}
|
|
102
|
-
/** Whether `value` is a prompt declaration. */
|
|
103
|
-
export declare function isPromptDeclaration(value: unknown): value is PromptDeclaration;
|
|
104
|
-
/**
|
|
105
|
-
* Prompt declarations for flags and arguments. The answer goes through the same schema as a
|
|
106
|
-
* value typed on the command line, and a failed check keeps the prompt open with the issue
|
|
107
|
-
* under it.
|
|
108
|
-
*
|
|
109
|
-
* @example flag.string("template", { default: "app", prompt: prompt.select({ message: "Which template?", options: TEMPLATES }) })
|
|
110
|
-
* @example argument(s.string(), { prompt: prompt.text({ message: "Where should the project go?" }) })
|
|
111
|
-
*/
|
|
112
|
-
export declare const prompt: {
|
|
113
|
-
text: (options: TextOptions) => PromptDeclaration<"text">;
|
|
114
|
-
password: (options: PasswordOptions) => PromptDeclaration<"password">;
|
|
115
|
-
number: (options: NumberOptions) => PromptDeclaration<"number">;
|
|
116
|
-
confirm: (options: ConfirmOptions) => PromptDeclaration<"confirm">;
|
|
117
|
-
select: (options: SelectOptions<unknown>) => PromptDeclaration<"select">;
|
|
118
|
-
multiselect: (options: MultiselectOptions<unknown>) => PromptDeclaration<"multiselect">;
|
|
119
|
-
search: (options: SearchOptions<unknown>) => PromptDeclaration<"search">;
|
|
120
|
-
};
|
|
121
|
-
/** What a handler-called prompt adds to a declaration's options. */
|
|
122
|
-
export interface AskOptions<T> {
|
|
123
|
-
/** The answer without a terminal, so the command still runs in CI or under an agent. */
|
|
124
|
-
default: T;
|
|
125
|
-
/**
|
|
126
|
-
* What a terminal starts from, preselected or filled in, when it differs from `default`: a
|
|
127
|
-
* question can answer no in CI and still take yes on Enter.
|
|
128
|
-
* @default the `default`
|
|
129
|
-
*/
|
|
130
|
-
initial?: T;
|
|
131
|
-
/** The name a test's scripted `answers` object answers this prompt by. */
|
|
132
|
-
name?: string;
|
|
133
|
-
/** Checks the answer; a failed check keeps the prompt open with the issue under it. */
|
|
134
|
-
schema?: StandardSchemaV1<T, unknown>;
|
|
135
|
-
}
|
|
136
|
-
/** Why a handler-called prompt has no answer: the user cancelled, or a test left it unscripted. */
|
|
137
|
-
export type PromptError = CancelledError | Error;
|
|
138
|
-
/** Prompts a handler asks itself, for questions that depend on earlier work. */
|
|
139
|
-
export interface Prompter {
|
|
140
|
-
text(options: TextOptions & AskOptions<string>): Promise<Result<string, PromptError>>;
|
|
141
|
-
password(options: PasswordOptions & AskOptions<string>): Promise<Result<string, PromptError>>;
|
|
142
|
-
number(options: NumberOptions & AskOptions<number>): Promise<Result<number, PromptError>>;
|
|
143
|
-
confirm(options: ConfirmOptions & AskOptions<boolean>): Promise<Result<boolean, PromptError>>;
|
|
144
|
-
select<T>(options: SelectOptions<T> & AskOptions<NoInfer<T>>): Promise<Result<T, PromptError>>;
|
|
145
|
-
multiselect<T>(options: MultiselectOptions<T> & AskOptions<readonly NoInfer<T>[]>): Promise<Result<T[], PromptError>>;
|
|
146
|
-
search<T>(options: SearchOptions<T> & {
|
|
147
|
-
multiple: true;
|
|
148
|
-
} & AskOptions<readonly NoInfer<T>[]>): Promise<Result<T[], PromptError>>;
|
|
149
|
-
search<T>(options: SearchOptions<T> & AskOptions<NoInfer<T>>): Promise<Result<T, PromptError>>;
|
|
150
|
-
}
|
|
151
|
-
/** The choices of a list prompt with every label and flag filled in. */
|
|
152
|
-
export declare function normalizeChoices<T>(choices: Choices<T>): Required<Choice<T>>[];
|
|
153
|
-
export {};
|
package/dist/prompt.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The seven prompt kinds as declarations: what a flag or argument asks when the command line
|
|
3
|
-
* leaves it out, and the option shapes `ctx.prompt` takes when a handler asks on its own. A
|
|
4
|
-
* declaration runs nothing; the run decides whether to ask, answer from a default or fail.
|
|
5
|
-
*
|
|
6
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
-
* @copyright Sergio Xalambrí 2026
|
|
8
|
-
*/
|
|
9
|
-
/** Brands a prompt declaration so a flag can tell it from a plain options object. */
|
|
10
|
-
const PROMPT = Symbol.for("@sdxc/cli.prompt");
|
|
11
|
-
/** Whether `value` is a prompt declaration. */
|
|
12
|
-
export function isPromptDeclaration(value) {
|
|
13
|
-
return typeof value === "object" && value !== null && PROMPT in value;
|
|
14
|
-
}
|
|
15
|
-
/** Builds one prompt declaration. */
|
|
16
|
-
function declare(kind) {
|
|
17
|
-
return (options) => ({
|
|
18
|
-
[PROMPT]: true,
|
|
19
|
-
kind,
|
|
20
|
-
options,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Prompt declarations for flags and arguments. The answer goes through the same schema as a
|
|
25
|
-
* value typed on the command line, and a failed check keeps the prompt open with the issue
|
|
26
|
-
* under it.
|
|
27
|
-
*
|
|
28
|
-
* @example flag.string("template", { default: "app", prompt: prompt.select({ message: "Which template?", options: TEMPLATES }) })
|
|
29
|
-
* @example argument(s.string(), { prompt: prompt.text({ message: "Where should the project go?" }) })
|
|
30
|
-
*/
|
|
31
|
-
export const prompt = {
|
|
32
|
-
text: declare("text"),
|
|
33
|
-
password: declare("password"),
|
|
34
|
-
number: declare("number"),
|
|
35
|
-
confirm: declare("confirm"),
|
|
36
|
-
select: declare("select"),
|
|
37
|
-
multiselect: declare("multiselect"),
|
|
38
|
-
search: declare("search"),
|
|
39
|
-
};
|
|
40
|
-
/** The choices of a list prompt with every label and flag filled in. */
|
|
41
|
-
export function normalizeChoices(choices) {
|
|
42
|
-
return choices.map((choice) => {
|
|
43
|
-
if (typeof choice === "object" && choice !== null && "value" in choice) {
|
|
44
|
-
return {
|
|
45
|
-
value: choice.value,
|
|
46
|
-
label: choice.label ?? String(choice.value),
|
|
47
|
-
hint: choice.hint ?? "",
|
|
48
|
-
disabled: choice.disabled ?? false,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
return { value: choice, label: String(choice), hint: "", disabled: false };
|
|
52
|
-
});
|
|
53
|
-
}
|
package/dist/resolve.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turns what the command line gave into the values a handler reads: each flag and argument
|
|
3
|
-
* converted, checked by its schema, asked for when missing on a terminal, or defaulted. Every
|
|
4
|
-
* value typed wrong is reported in one usage error before any question is asked.
|
|
5
|
-
*
|
|
6
|
-
* @author [Sergio Xalambrí](https://sergiodxa.com)
|
|
7
|
-
* @copyright Sergio Xalambrí 2026
|
|
8
|
-
*/
|
|
9
|
-
import type { Result } from "@sdxc/result";
|
|
10
|
-
import type { Parsed } from "./parse.js";
|
|
11
|
-
import type { Prompter } from "./prompt.js";
|
|
12
|
-
import type { Session } from "./session.js";
|
|
13
|
-
/** The values a handler reads. */
|
|
14
|
-
export interface Inputs {
|
|
15
|
-
arguments: Record<string, unknown>;
|
|
16
|
-
flags: Record<string, unknown>;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Resolves `parsed`'s command into the values its handler reads. The parser's own issues lead
|
|
20
|
-
* the usage error, followed by every value that failed its conversion or schema.
|
|
21
|
-
*
|
|
22
|
-
* @returns The inputs, a usage failure listing every problem, a cancelled failure, or the
|
|
23
|
-
* failure of a scripted answer that was missing or rejected
|
|
24
|
-
*/
|
|
25
|
-
export declare function resolveInputs(parsed: Parsed, session: Session): Promise<Result<Inputs, Error>>;
|
|
26
|
-
/** The `ctx.prompt` of one run: asks on a terminal or from a script, else answers the default. */
|
|
27
|
-
export declare function createPrompter(session: Session): Prompter;
|