@rune-cli/rune 0.0.9 → 0.0.11
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.mjs +227 -78
- package/dist/{dist-DuisScgY.mjs → dist-Bpf2xVvb.mjs} +59 -72
- package/dist/{index-BWxfSwrT.d.mts → index-C179V2IJ.d.mts} +48 -7
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{run-manifest-command-BphalAwU.mjs → run-manifest-command-DepwxrFI.mjs} +68 -34
- package/dist/runtime.d.mts +4 -3
- package/dist/runtime.mjs +2 -2
- package/dist/test.d.mts +209 -42
- package/dist/test.mjs +88 -39
- package/package.json +3 -2
|
@@ -117,21 +117,21 @@ interface SchemaFieldBase<TName extends string, TSchema extends StandardSchemaV1
|
|
|
117
117
|
readonly default?: never;
|
|
118
118
|
}
|
|
119
119
|
interface PrimitiveArgField<TName extends string = string, TType extends PrimitiveFieldType = PrimitiveFieldType> extends PrimitiveFieldBase<TName, TType> {
|
|
120
|
-
readonly
|
|
120
|
+
readonly short?: never;
|
|
121
121
|
readonly flag?: never;
|
|
122
122
|
}
|
|
123
123
|
interface SchemaArgField<TName extends string = string, TSchema extends StandardSchemaV1 = StandardSchemaV1> extends SchemaFieldBase<TName, TSchema> {
|
|
124
|
-
readonly
|
|
124
|
+
readonly short?: never;
|
|
125
125
|
readonly flag?: never;
|
|
126
126
|
}
|
|
127
127
|
interface PrimitiveOptionField<TName extends string = string, TType extends PrimitiveFieldType = PrimitiveFieldType> extends PrimitiveFieldBase<TName, TType> {
|
|
128
128
|
/** Single-character shorthand (e.g. `"v"` for `--verbose` → `-v`). */
|
|
129
|
-
readonly
|
|
129
|
+
readonly short?: string | undefined;
|
|
130
130
|
readonly flag?: never;
|
|
131
131
|
}
|
|
132
132
|
interface SchemaOptionField<TName extends string = string, TSchema extends StandardSchemaV1 = StandardSchemaV1> extends SchemaFieldBase<TName, TSchema> {
|
|
133
133
|
/** Single-character shorthand (e.g. `"v"` for `--verbose` → `-v`). */
|
|
134
|
-
readonly
|
|
134
|
+
readonly short?: string | undefined;
|
|
135
135
|
/**
|
|
136
136
|
* When `true`, the option is parsed as a boolean flag (no value expected).
|
|
137
137
|
* The schema receives `true` when the flag is present, `undefined` when absent.
|
|
@@ -201,6 +201,12 @@ interface CommandContext<TOptions, TArgs> {
|
|
|
201
201
|
interface DefineCommandInput<TArgsFields extends readonly CommandArgField[] | undefined = undefined, TOptionsFields extends readonly CommandOptionField[] | undefined = undefined> {
|
|
202
202
|
/** One-line summary shown in `--help` output. */
|
|
203
203
|
readonly description?: string | undefined;
|
|
204
|
+
/**
|
|
205
|
+
* Alternative names for this command. Each alias is an additional path
|
|
206
|
+
* segment that routes to this command. Aliases must follow kebab-case
|
|
207
|
+
* rules (lowercase letters, digits, and internal hyphens).
|
|
208
|
+
*/
|
|
209
|
+
readonly aliases?: readonly string[] | undefined;
|
|
204
210
|
/**
|
|
205
211
|
* Positional arguments declared in the order they appear on the command line.
|
|
206
212
|
* Required arguments must come before optional ones.
|
|
@@ -211,7 +217,7 @@ interface DefineCommandInput<TArgsFields extends readonly CommandArgField[] | un
|
|
|
211
217
|
*/
|
|
212
218
|
readonly args?: TArgsFields;
|
|
213
219
|
/**
|
|
214
|
-
* Options declared as `--name` flags, with optional single-character
|
|
220
|
+
* Options declared as `--name` flags, with optional single-character short forms.
|
|
215
221
|
* Option names must be unique within the command, start with a letter, and
|
|
216
222
|
* contain only letters, numbers, and internal hyphens.
|
|
217
223
|
*
|
|
@@ -227,6 +233,7 @@ interface DefineCommandInput<TArgsFields extends readonly CommandArgField[] | un
|
|
|
227
233
|
}
|
|
228
234
|
interface DefinedCommand<TArgsFields extends readonly CommandArgField[] = readonly [], TOptionsFields extends readonly CommandOptionField[] = readonly []> {
|
|
229
235
|
readonly description?: string | undefined;
|
|
236
|
+
readonly aliases: readonly string[];
|
|
230
237
|
readonly args: TArgsFields;
|
|
231
238
|
readonly options: TOptionsFields;
|
|
232
239
|
readonly run: (ctx: CommandContext<InferNamedFields<TOptionsFields, true>, InferNamedFields<TArgsFields>>) => void | Promise<void>;
|
|
@@ -246,7 +253,7 @@ interface DefinedCommand<TArgsFields extends readonly CommandArgField[] = readon
|
|
|
246
253
|
* { name: "name", type: "string", required: true },
|
|
247
254
|
* ],
|
|
248
255
|
* options: [
|
|
249
|
-
* { name: "loud", type: "boolean",
|
|
256
|
+
* { name: "loud", type: "boolean", short: "l" },
|
|
250
257
|
* ],
|
|
251
258
|
* run(ctx) {
|
|
252
259
|
* const greeting = `Hello, ${ctx.args.name}!`;
|
|
@@ -285,6 +292,40 @@ interface DefinedCommand<TArgsFields extends readonly CommandArgField[] = readon
|
|
|
285
292
|
*/
|
|
286
293
|
declare function defineCommand<const TArgsFields extends readonly CommandArgField[] | undefined = undefined, const TOptionsFields extends readonly CommandOptionField[] | undefined = undefined>(input: DefineCommandInput<TArgsFields, TOptionsFields> & ValidateArgOrder<TArgsFields>): DefinedCommand<NormalizeFields<TArgsFields, CommandArgField>, NormalizeFields<TOptionsFields, CommandOptionField>>;
|
|
287
294
|
//#endregion
|
|
295
|
+
//#region src/define-group.d.ts
|
|
296
|
+
/** The group definition object accepted by {@link defineGroup}. */
|
|
297
|
+
interface DefineGroupInput {
|
|
298
|
+
/** One-line summary shown in `--help` output. */
|
|
299
|
+
readonly description: string;
|
|
300
|
+
/**
|
|
301
|
+
* Alternative names for this command group. Each alias is an additional path
|
|
302
|
+
* segment that routes to this group. Aliases must follow kebab-case rules
|
|
303
|
+
* (lowercase letters, digits, and internal hyphens).
|
|
304
|
+
*/
|
|
305
|
+
readonly aliases?: readonly string[] | undefined;
|
|
306
|
+
}
|
|
307
|
+
/** The normalized group object returned by `defineGroup`. */
|
|
308
|
+
interface DefinedGroup {
|
|
309
|
+
readonly description: string;
|
|
310
|
+
readonly aliases: readonly string[];
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Defines metadata for a command group (a directory that only groups
|
|
314
|
+
* subcommands without being executable itself).
|
|
315
|
+
*
|
|
316
|
+
* Place the default export of this function in a `_group.ts` file inside a
|
|
317
|
+
* command directory.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* // src/commands/project/_group.ts
|
|
322
|
+
* export default defineGroup({
|
|
323
|
+
* description: "Manage projects",
|
|
324
|
+
* });
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
declare function defineGroup(input: DefineGroupInput): DefinedGroup;
|
|
328
|
+
//#endregion
|
|
288
329
|
//#region src/execute-command.d.ts
|
|
289
330
|
interface ExecuteCommandInput<TOptions, TArgs> {
|
|
290
331
|
readonly options?: TOptions | undefined;
|
|
@@ -293,4 +334,4 @@ interface ExecuteCommandInput<TOptions, TArgs> {
|
|
|
293
334
|
readonly rawArgs?: readonly string[] | undefined;
|
|
294
335
|
}
|
|
295
336
|
//#endregion
|
|
296
|
-
export {
|
|
337
|
+
export { DefinedCommand as a, InferExecutionFields as c, PrimitiveOptionField as d, SchemaArgField as f, defineGroup as h, DefineGroupInput as i, PrimitiveArgField as l, defineCommand as m, CommandContext as n, DefinedGroup as o, SchemaOptionField as p, CommandOptionField as r, ExecuteCommandInput as s, CommandArgField as t, PrimitiveFieldType as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { type CommandArgField, type CommandContext, type CommandOptionField, type DefinedCommand, type ExecuteCommandInput, type InferExecutionFields, type PrimitiveArgField, type PrimitiveFieldType, type PrimitiveOptionField, type SchemaArgField, type SchemaOptionField, defineCommand };
|
|
1
|
+
import { a as DefinedCommand, c as InferExecutionFields, d as PrimitiveOptionField, f as SchemaArgField, h as defineGroup, i as DefineGroupInput, l as PrimitiveArgField, m as defineCommand, n as CommandContext, o as DefinedGroup, p as SchemaOptionField, r as CommandOptionField, s as ExecuteCommandInput, t as CommandArgField, u as PrimitiveFieldType } from "./index-C179V2IJ.mjs";
|
|
2
|
+
export { type CommandArgField, type CommandContext, type CommandOptionField, type DefineGroupInput, type DefinedCommand, type DefinedGroup, type ExecuteCommandInput, type InferExecutionFields, type PrimitiveArgField, type PrimitiveFieldType, type PrimitiveOptionField, type SchemaArgField, type SchemaOptionField, defineCommand, defineGroup };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as defineCommand } from "./dist-
|
|
2
|
-
export { defineCommand };
|
|
1
|
+
import { n as defineGroup, t as defineCommand } from "./dist-Bpf2xVvb.mjs";
|
|
2
|
+
export { defineCommand, defineGroup };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as isSchemaField, i as isDefinedCommand, o as
|
|
1
|
+
import { a as isSchemaField, i as isDefinedCommand, o as parseCommandArgs, r as executeCommand } from "./dist-Bpf2xVvb.mjs";
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
//#region src/cli/flags.ts
|
|
4
4
|
function isHelpFlag(token) {
|
|
@@ -8,7 +8,7 @@ function isVersionFlag(token) {
|
|
|
8
8
|
return token === "--version" || token === "-V";
|
|
9
9
|
}
|
|
10
10
|
//#endregion
|
|
11
|
-
//#region src/manifest/command-loader.ts
|
|
11
|
+
//#region src/manifest/runtime/command-loader.ts
|
|
12
12
|
async function loadCommandFromModule(sourceFilePath) {
|
|
13
13
|
const loadedModule = await import(pathToFileURL(sourceFilePath).href);
|
|
14
14
|
if (loadedModule.default === void 0) throw new Error(`Command module did not export a default command: ${sourceFilePath}`);
|
|
@@ -29,7 +29,23 @@ function describeCommandModuleExport(value) {
|
|
|
29
29
|
}
|
|
30
30
|
const defaultLoadCommand = (node) => loadCommandFromModule(node.sourceFilePath);
|
|
31
31
|
//#endregion
|
|
32
|
-
//#region src/manifest/
|
|
32
|
+
//#region src/manifest/manifest-map.ts
|
|
33
|
+
function commandManifestPathToKey(pathSegments) {
|
|
34
|
+
return pathSegments.join(" ");
|
|
35
|
+
}
|
|
36
|
+
function createCommandManifestNodeMap(manifest) {
|
|
37
|
+
const entries = [];
|
|
38
|
+
for (const node of manifest.nodes) {
|
|
39
|
+
entries.push([commandManifestPathToKey(node.pathSegments), node]);
|
|
40
|
+
if (node.aliases.length > 0 && node.pathSegments.length > 0) {
|
|
41
|
+
const parentSegments = node.pathSegments.slice(0, -1);
|
|
42
|
+
for (const alias of node.aliases) entries.push([commandManifestPathToKey([...parentSegments, alias]), node]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return Object.fromEntries(entries);
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/manifest/runtime/damerau-levenshtein.ts
|
|
33
49
|
function damerauLevenshteinDistance(left, right) {
|
|
34
50
|
const rows = left.length + 1;
|
|
35
51
|
const cols = right.length + 1;
|
|
@@ -44,15 +60,7 @@ function damerauLevenshteinDistance(left, right) {
|
|
|
44
60
|
return matrix[left.length][right.length];
|
|
45
61
|
}
|
|
46
62
|
//#endregion
|
|
47
|
-
//#region src/manifest/
|
|
48
|
-
function commandManifestPathToKey(pathSegments) {
|
|
49
|
-
return pathSegments.join(" ");
|
|
50
|
-
}
|
|
51
|
-
function createCommandManifestNodeMap(manifest) {
|
|
52
|
-
return Object.fromEntries(manifest.nodes.map((node) => [commandManifestPathToKey(node.pathSegments), node]));
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
//#region src/manifest/resolve-command-path.ts
|
|
63
|
+
//#region src/manifest/runtime/resolve-command-route.ts
|
|
56
64
|
function isOptionLikeToken(token) {
|
|
57
65
|
return token === "--" || token.startsWith("-");
|
|
58
66
|
}
|
|
@@ -62,13 +70,35 @@ function getHelpRequested(args) {
|
|
|
62
70
|
function getSuggestionThreshold(candidate) {
|
|
63
71
|
return Math.max(2, Math.floor(candidate.length / 3));
|
|
64
72
|
}
|
|
65
|
-
function getSuggestedChildNames(unknownSegment,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
distance: damerauLevenshteinDistance(unknownSegment,
|
|
69
|
-
|
|
73
|
+
function getSuggestedChildNames(unknownSegment, candidates) {
|
|
74
|
+
const scored = candidates.map((candidate) => ({
|
|
75
|
+
canonicalName: candidate.canonicalName,
|
|
76
|
+
distance: damerauLevenshteinDistance(unknownSegment, candidate.matchName),
|
|
77
|
+
threshold: getSuggestionThreshold(candidate.matchName)
|
|
78
|
+
})).filter(({ distance, threshold }) => distance <= threshold);
|
|
79
|
+
const bestByCanonical = /* @__PURE__ */ new Map();
|
|
80
|
+
for (const entry of scored) {
|
|
81
|
+
const existing = bestByCanonical.get(entry.canonicalName);
|
|
82
|
+
if (existing === void 0 || entry.distance < existing) bestByCanonical.set(entry.canonicalName, entry.distance);
|
|
83
|
+
}
|
|
84
|
+
return [...bestByCanonical.entries()].sort(([nameA, distA], [nameB, distB]) => distA - distB || nameA.localeCompare(nameB)).slice(0, 3).map(([name]) => name);
|
|
85
|
+
}
|
|
86
|
+
function collectSiblingCandidates(currentNode, nodeMap) {
|
|
87
|
+
const candidates = [];
|
|
88
|
+
for (const childName of currentNode.childNames) {
|
|
89
|
+
candidates.push({
|
|
90
|
+
canonicalName: childName,
|
|
91
|
+
matchName: childName
|
|
92
|
+
});
|
|
93
|
+
const childNode = nodeMap[commandManifestPathToKey([...currentNode.pathSegments, childName])];
|
|
94
|
+
if (childNode) for (const alias of childNode.aliases) candidates.push({
|
|
95
|
+
canonicalName: childName,
|
|
96
|
+
matchName: alias
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return candidates;
|
|
70
100
|
}
|
|
71
|
-
function
|
|
101
|
+
function resolveCommandRoute(manifest, rawArgs) {
|
|
72
102
|
const nodeMap = createCommandManifestNodeMap(manifest);
|
|
73
103
|
const rootNode = nodeMap[""];
|
|
74
104
|
if (rootNode === void 0) throw new Error("Manifest root node is missing");
|
|
@@ -79,7 +109,7 @@ function resolveCommandPath(manifest, rawArgs) {
|
|
|
79
109
|
if (isOptionLikeToken(token)) break;
|
|
80
110
|
const childNode = nodeMap[commandManifestPathToKey([...currentNode.pathSegments, token])];
|
|
81
111
|
if (childNode === void 0) {
|
|
82
|
-
const suggestions = getSuggestedChildNames(token, currentNode
|
|
112
|
+
const suggestions = getSuggestedChildNames(token, collectSiblingCandidates(currentNode, nodeMap));
|
|
83
113
|
if (currentNode.kind === "group" || suggestions.length > 0) return {
|
|
84
114
|
kind: "unknown",
|
|
85
115
|
attemptedPath: [...currentNode.pathSegments, token],
|
|
@@ -111,7 +141,7 @@ function resolveCommandPath(manifest, rawArgs) {
|
|
|
111
141
|
};
|
|
112
142
|
}
|
|
113
143
|
//#endregion
|
|
114
|
-
//#region src/manifest/render-help.ts
|
|
144
|
+
//#region src/manifest/runtime/render-help.ts
|
|
115
145
|
function formatCommandName(cliName, pathSegments) {
|
|
116
146
|
return pathSegments.length === 0 ? cliName : `${cliName} ${pathSegments.join(" ")}`;
|
|
117
147
|
}
|
|
@@ -126,8 +156,8 @@ function formatArgumentLabel(field) {
|
|
|
126
156
|
}
|
|
127
157
|
function formatOptionLabel(field) {
|
|
128
158
|
const longOptionLabel = `--${field.name}${formatTypeHint(field)}`;
|
|
129
|
-
if (!field.
|
|
130
|
-
return `-${field.
|
|
159
|
+
if (!field.short) return longOptionLabel;
|
|
160
|
+
return `-${field.short}, ${longOptionLabel}`;
|
|
131
161
|
}
|
|
132
162
|
async function isFieldRequired(field) {
|
|
133
163
|
if (!isSchemaField(field)) return field.required === true && field.default === void 0;
|
|
@@ -148,12 +178,16 @@ function renderGroupHelp(options) {
|
|
|
148
178
|
const { manifest, node, cliName, version } = options;
|
|
149
179
|
const nodeMap = createCommandManifestNodeMap(manifest);
|
|
150
180
|
const entries = node.childNames.map((childName) => {
|
|
181
|
+
const childNode = nodeMap[commandManifestPathToKey([...node.pathSegments, childName])];
|
|
151
182
|
return {
|
|
152
|
-
label: childName,
|
|
153
|
-
description:
|
|
183
|
+
label: `${childName}${childNode && childNode.aliases.length > 0 ? ` (${childNode.aliases.join(", ")})` : ""}`,
|
|
184
|
+
description: childNode?.description
|
|
154
185
|
};
|
|
155
186
|
});
|
|
156
|
-
const
|
|
187
|
+
const commandName = formatCommandName(cliName, node.pathSegments);
|
|
188
|
+
const parts = [];
|
|
189
|
+
if (node.description) parts.push(node.description);
|
|
190
|
+
parts.push(`Usage: ${commandName} <command>`);
|
|
157
191
|
if (entries.length > 0) parts.push(`Subcommands:\n${formatSectionEntries(entries)}`);
|
|
158
192
|
const optionEntries = [{
|
|
159
193
|
label: "-h, --help",
|
|
@@ -194,7 +228,7 @@ function renderUnknownCommandMessage(route, cliName) {
|
|
|
194
228
|
return `${parts.join("\n\n")}\n`;
|
|
195
229
|
}
|
|
196
230
|
//#endregion
|
|
197
|
-
//#region src/manifest/resolve-help.ts
|
|
231
|
+
//#region src/manifest/runtime/resolve-help.ts
|
|
198
232
|
async function renderResolvedHelp(options) {
|
|
199
233
|
if (options.route.kind === "unknown") return renderUnknownCommandMessage(options.route, options.cliName);
|
|
200
234
|
if (options.route.kind === "group") return renderGroupHelp({
|
|
@@ -206,7 +240,7 @@ async function renderResolvedHelp(options) {
|
|
|
206
240
|
return renderCommandHelp(await (options.loadCommand ?? defaultLoadCommand)(options.route.node), options.route.matchedPath, options.cliName);
|
|
207
241
|
}
|
|
208
242
|
//#endregion
|
|
209
|
-
//#region src/manifest/run-manifest-command.ts
|
|
243
|
+
//#region src/manifest/runtime/run-manifest-command.ts
|
|
210
244
|
function ensureTrailingNewline(text) {
|
|
211
245
|
return text.endsWith("\n") ? text : `${text}\n`;
|
|
212
246
|
}
|
|
@@ -221,7 +255,7 @@ async function runManifestCommand(options) {
|
|
|
221
255
|
process.stdout.write(`${options.cliName} v${options.version}\n`);
|
|
222
256
|
return 0;
|
|
223
257
|
}
|
|
224
|
-
const route =
|
|
258
|
+
const route = resolveCommandRoute(options.manifest, options.rawArgs);
|
|
225
259
|
if (route.kind === "unknown" || route.kind === "group" || route.helpRequested) {
|
|
226
260
|
const output = await renderResolvedHelp({
|
|
227
261
|
manifest: options.manifest,
|
|
@@ -238,16 +272,16 @@ async function runManifestCommand(options) {
|
|
|
238
272
|
return 0;
|
|
239
273
|
}
|
|
240
274
|
const command = await (options.loadCommand ?? defaultLoadCommand)(route.node);
|
|
241
|
-
const
|
|
242
|
-
if (!
|
|
243
|
-
process.stderr.write(ensureTrailingNewline(
|
|
275
|
+
const commandInput = await parseCommandArgs(command, route.remainingArgs);
|
|
276
|
+
if (!commandInput.ok) {
|
|
277
|
+
process.stderr.write(ensureTrailingNewline(commandInput.error.message));
|
|
244
278
|
return 1;
|
|
245
279
|
}
|
|
246
280
|
const result = await executeCommand(command, {
|
|
247
|
-
options:
|
|
248
|
-
args:
|
|
281
|
+
options: commandInput.value.options,
|
|
282
|
+
args: commandInput.value.args,
|
|
249
283
|
cwd: options.cwd,
|
|
250
|
-
rawArgs:
|
|
284
|
+
rawArgs: commandInput.value.rawArgs
|
|
251
285
|
});
|
|
252
286
|
if (result.errorMessage) process.stderr.write(ensureTrailingNewline(result.errorMessage));
|
|
253
287
|
return result.exitCode;
|
package/dist/runtime.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as DefinedCommand, r as CommandOptionField, t as CommandArgField } from "./index-C179V2IJ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/manifest/manifest-types.d.ts
|
|
4
4
|
type CommandManifestPath = readonly string[];
|
|
@@ -7,6 +7,7 @@ interface CommandManifestNodeBase {
|
|
|
7
7
|
readonly pathSegments: CommandManifestPath;
|
|
8
8
|
readonly kind: CommandManifestNodeKind;
|
|
9
9
|
readonly childNames: readonly string[];
|
|
10
|
+
readonly aliases: readonly string[];
|
|
10
11
|
readonly description?: string | undefined;
|
|
11
12
|
}
|
|
12
13
|
interface CommandManifestCommandNode extends CommandManifestNodeBase {
|
|
@@ -22,10 +23,10 @@ interface CommandManifest {
|
|
|
22
23
|
readonly nodes: readonly CommandManifestNode[];
|
|
23
24
|
}
|
|
24
25
|
//#endregion
|
|
25
|
-
//#region src/manifest/command-loader.d.ts
|
|
26
|
+
//#region src/manifest/runtime/command-loader.d.ts
|
|
26
27
|
type LoadCommandFn = (node: CommandManifestCommandNode) => Promise<DefinedCommand<readonly CommandArgField[], readonly CommandOptionField[]>>;
|
|
27
28
|
//#endregion
|
|
28
|
-
//#region src/manifest/run-manifest-command.d.ts
|
|
29
|
+
//#region src/manifest/runtime/run-manifest-command.d.ts
|
|
29
30
|
interface RunManifestCommandOptions {
|
|
30
31
|
readonly manifest: CommandManifest;
|
|
31
32
|
readonly rawArgs: readonly string[];
|
package/dist/runtime.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "./dist-
|
|
2
|
-
import { t as runManifestCommand } from "./run-manifest-command-
|
|
1
|
+
import "./dist-Bpf2xVvb.mjs";
|
|
2
|
+
import { t as runManifestCommand } from "./run-manifest-command-DepwxrFI.mjs";
|
|
3
3
|
export { runManifestCommand };
|
package/dist/test.d.mts
CHANGED
|
@@ -1,6 +1,210 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//#region
|
|
1
|
+
//#region ../test-utils/dist/index.d.mts
|
|
2
|
+
//#endregion
|
|
3
|
+
//#region ../core/dist/index.d.mts
|
|
4
|
+
//#region ../../node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
|
|
5
|
+
/** The Standard Typed interface. This is a base type extended by other specs. */
|
|
6
|
+
interface StandardTypedV1<Input = unknown, Output = Input> {
|
|
7
|
+
/** The Standard properties. */
|
|
8
|
+
readonly "~standard": StandardTypedV1.Props<Input, Output>;
|
|
9
|
+
}
|
|
10
|
+
declare namespace StandardTypedV1 {
|
|
11
|
+
/** The Standard Typed properties interface. */
|
|
12
|
+
interface Props<Input = unknown, Output = Input> {
|
|
13
|
+
/** The version number of the standard. */
|
|
14
|
+
readonly version: 1;
|
|
15
|
+
/** The vendor name of the schema library. */
|
|
16
|
+
readonly vendor: string;
|
|
17
|
+
/** Inferred types associated with the schema. */
|
|
18
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** The Standard Typed types interface. */
|
|
21
|
+
interface Types<Input = unknown, Output = Input> {
|
|
22
|
+
/** The input type of the schema. */
|
|
23
|
+
readonly input: Input;
|
|
24
|
+
/** The output type of the schema. */
|
|
25
|
+
readonly output: Output;
|
|
26
|
+
}
|
|
27
|
+
/** Infers the input type of a Standard Typed. */
|
|
28
|
+
type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
|
|
29
|
+
/** Infers the output type of a Standard Typed. */
|
|
30
|
+
type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
|
|
31
|
+
}
|
|
32
|
+
/** The Standard Schema interface. */
|
|
33
|
+
interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
34
|
+
/** The Standard Schema properties. */
|
|
35
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>;
|
|
36
|
+
}
|
|
37
|
+
declare namespace StandardSchemaV1 {
|
|
38
|
+
/** The Standard Schema properties interface. */
|
|
39
|
+
interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
|
40
|
+
/** Validates unknown input values. */
|
|
41
|
+
readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
|
|
42
|
+
}
|
|
43
|
+
/** The result interface of the validate function. */
|
|
44
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
45
|
+
/** The result interface if validation succeeds. */
|
|
46
|
+
interface SuccessResult<Output> {
|
|
47
|
+
/** The typed output value. */
|
|
48
|
+
readonly value: Output;
|
|
49
|
+
/** A falsy value for `issues` indicates success. */
|
|
50
|
+
readonly issues?: undefined;
|
|
51
|
+
}
|
|
52
|
+
interface Options {
|
|
53
|
+
/** Explicit support for additional vendor-specific parameters, if needed. */
|
|
54
|
+
readonly libraryOptions?: Record<string, unknown> | undefined;
|
|
55
|
+
}
|
|
56
|
+
/** The result interface if validation fails. */
|
|
57
|
+
interface FailureResult {
|
|
58
|
+
/** The issues of failed validation. */
|
|
59
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
60
|
+
}
|
|
61
|
+
/** The issue interface of the failure output. */
|
|
62
|
+
interface Issue {
|
|
63
|
+
/** The error message of the issue. */
|
|
64
|
+
readonly message: string;
|
|
65
|
+
/** The path of the issue, if any. */
|
|
66
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
67
|
+
}
|
|
68
|
+
/** The path segment interface of the issue. */
|
|
69
|
+
interface PathSegment {
|
|
70
|
+
/** The key representing a path segment. */
|
|
71
|
+
readonly key: PropertyKey;
|
|
72
|
+
}
|
|
73
|
+
/** The Standard types interface. */
|
|
74
|
+
interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
|
|
75
|
+
/** Infers the input type of a Standard. */
|
|
76
|
+
type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
|
77
|
+
/** Infers the output type of a Standard. */
|
|
78
|
+
type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
|
79
|
+
}
|
|
80
|
+
/** The Standard JSON Schema interface. */
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/command-types.d.ts
|
|
83
|
+
type PrimitiveFieldType = "string" | "number" | "boolean";
|
|
84
|
+
type PrimitiveFieldValue<TType extends PrimitiveFieldType> = TType extends "string" ? string : TType extends "number" ? number : boolean;
|
|
85
|
+
interface NamedField<TName extends string = string> {
|
|
86
|
+
/**
|
|
87
|
+
* Identifier used as the key in `ctx.args` / `ctx.options`.
|
|
88
|
+
*
|
|
89
|
+
* For args, any non-empty name is allowed.
|
|
90
|
+
* For options, names must start with a letter and may contain only letters,
|
|
91
|
+
* numbers, and internal hyphens (for example: `dry-run`, `dryRun`, `v2`).
|
|
92
|
+
*/
|
|
93
|
+
readonly name: TName;
|
|
94
|
+
/** One-line help text shown in `--help` output. */
|
|
95
|
+
readonly description?: string | undefined;
|
|
96
|
+
}
|
|
97
|
+
interface PrimitiveFieldBase<TName extends string, TType extends PrimitiveFieldType> extends NamedField<TName> {
|
|
98
|
+
/** Primitive type that Rune parses the raw CLI token into (`"string"`, `"number"`, or `"boolean"`). */
|
|
99
|
+
readonly type: TType;
|
|
100
|
+
/**
|
|
101
|
+
* When `true`, the field must be provided by the user.
|
|
102
|
+
* Omitted or `false` makes the field optional. Absent fields are `undefined`
|
|
103
|
+
* in `ctx`, except primitive boolean options, which default to `false`.
|
|
104
|
+
*/
|
|
105
|
+
readonly required?: boolean | undefined;
|
|
106
|
+
/** Value used when the user does not provide this field. Makes the field always present in `ctx`. */
|
|
107
|
+
readonly default?: PrimitiveFieldValue<TType> | undefined;
|
|
108
|
+
readonly schema?: never;
|
|
109
|
+
}
|
|
110
|
+
interface SchemaFieldBase<TName extends string, TSchema extends StandardSchemaV1 = StandardSchemaV1> extends NamedField<TName> {
|
|
111
|
+
/**
|
|
112
|
+
* A Standard Schema object (e.g. `z.string()`, `v.number()`) used to
|
|
113
|
+
* validate and transform the raw CLI token. Required/optional and default
|
|
114
|
+
* semantics are derived from the schema itself.
|
|
115
|
+
*/
|
|
116
|
+
readonly schema: TSchema;
|
|
117
|
+
readonly type?: never;
|
|
118
|
+
readonly required?: never;
|
|
119
|
+
readonly default?: never;
|
|
120
|
+
}
|
|
121
|
+
interface PrimitiveArgField<TName extends string = string, TType extends PrimitiveFieldType = PrimitiveFieldType> extends PrimitiveFieldBase<TName, TType> {
|
|
122
|
+
readonly short?: never;
|
|
123
|
+
readonly flag?: never;
|
|
124
|
+
}
|
|
125
|
+
interface SchemaArgField<TName extends string = string, TSchema extends StandardSchemaV1 = StandardSchemaV1> extends SchemaFieldBase<TName, TSchema> {
|
|
126
|
+
readonly short?: never;
|
|
127
|
+
readonly flag?: never;
|
|
128
|
+
}
|
|
129
|
+
interface PrimitiveOptionField<TName extends string = string, TType extends PrimitiveFieldType = PrimitiveFieldType> extends PrimitiveFieldBase<TName, TType> {
|
|
130
|
+
/** Single-character shorthand (e.g. `"v"` for `--verbose` → `-v`). */
|
|
131
|
+
readonly short?: string | undefined;
|
|
132
|
+
readonly flag?: never;
|
|
133
|
+
}
|
|
134
|
+
interface SchemaOptionField<TName extends string = string, TSchema extends StandardSchemaV1 = StandardSchemaV1> extends SchemaFieldBase<TName, TSchema> {
|
|
135
|
+
/** Single-character shorthand (e.g. `"v"` for `--verbose` → `-v`). */
|
|
136
|
+
readonly short?: string | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* When `true`, the option is parsed as a boolean flag (no value expected).
|
|
139
|
+
* The schema receives `true` when the flag is present, `undefined` when absent.
|
|
140
|
+
*/
|
|
141
|
+
readonly flag?: true | undefined;
|
|
142
|
+
}
|
|
143
|
+
type CommandArgField = PrimitiveArgField | SchemaArgField;
|
|
144
|
+
type CommandOptionField = PrimitiveOptionField | SchemaOptionField;
|
|
145
|
+
type FieldName<TField> = TField extends {
|
|
146
|
+
readonly name: infer TName extends string;
|
|
147
|
+
} ? TName : never;
|
|
148
|
+
type InferSchemaOutput<TSchema> = TSchema extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<TSchema> : never;
|
|
149
|
+
type InferSchemaInput<TSchema> = TSchema extends StandardSchemaV1 ? StandardSchemaV1.InferInput<TSchema> : never;
|
|
150
|
+
type IsOptionalSchemaOutput<TValue> = undefined extends TValue ? true : false;
|
|
151
|
+
type FieldValue<TField> = TField extends {
|
|
152
|
+
readonly schema: infer TSchema;
|
|
153
|
+
} ? Exclude<InferSchemaOutput<TSchema>, undefined> : TField extends {
|
|
154
|
+
readonly type: infer TType extends PrimitiveFieldType;
|
|
155
|
+
} ? PrimitiveFieldValue<TType> : never;
|
|
156
|
+
type FieldInputValue<TField> = TField extends {
|
|
157
|
+
readonly schema: infer TSchema;
|
|
158
|
+
} ? InferSchemaInput<TSchema> : TField extends {
|
|
159
|
+
readonly type: infer TType extends PrimitiveFieldType;
|
|
160
|
+
} ? PrimitiveFieldValue<TType> : never;
|
|
161
|
+
type HasDefaultValue<TField> = TField extends {
|
|
162
|
+
readonly default: infer TDefault;
|
|
163
|
+
} ? [TDefault] extends [undefined] ? false : true : false;
|
|
164
|
+
type IsRequiredField<TField, TBooleanAlwaysPresent extends boolean = false> = TField extends {
|
|
165
|
+
readonly schema: infer TSchema;
|
|
166
|
+
} ? IsOptionalSchemaOutput<InferSchemaOutput<TSchema>> extends true ? false : true : HasDefaultValue<TField> extends true ? true : TBooleanAlwaysPresent extends true ? TField extends {
|
|
167
|
+
readonly type: "boolean";
|
|
168
|
+
} ? true : TField extends {
|
|
169
|
+
readonly required: true;
|
|
170
|
+
} ? true : false : TField extends {
|
|
171
|
+
readonly required: true;
|
|
172
|
+
} ? true : false;
|
|
173
|
+
type Simplify<TValue> = { [TKey in keyof TValue]: TValue[TKey] };
|
|
174
|
+
type InferNamedFields<TFields extends readonly NamedField[], TBooleanAlwaysPresent extends boolean = false> = Simplify<{ [TField in TFields[number] as IsRequiredField<TField, TBooleanAlwaysPresent> extends true ? FieldName<TField> : never]: FieldValue<TField> } & { [TField in TFields[number] as IsRequiredField<TField, TBooleanAlwaysPresent> extends true ? never : FieldName<TField>]?: FieldValue<TField> }>;
|
|
175
|
+
type InferExecutionFields<TFields extends readonly NamedField[]> = Simplify<{ [TField in TFields[number] as FieldName<TField>]?: FieldInputValue<TField> }>;
|
|
176
|
+
/** Runtime data passed into a command's `run` function. */
|
|
177
|
+
interface CommandContext<TOptions, TArgs> {
|
|
178
|
+
/** Parsed and validated positional argument values keyed by field name. */
|
|
179
|
+
readonly args: TArgs;
|
|
180
|
+
/** Parsed and validated option values keyed by field name. */
|
|
181
|
+
readonly options: TOptions;
|
|
182
|
+
/** Working directory the CLI was invoked from. */
|
|
183
|
+
readonly cwd: string;
|
|
184
|
+
/**
|
|
185
|
+
* Unparsed argv tokens passed to this command, before Rune splits them
|
|
186
|
+
* into `args` and `options`. Useful for forwarding to child processes.
|
|
187
|
+
*/
|
|
188
|
+
readonly rawArgs: readonly string[];
|
|
189
|
+
}
|
|
190
|
+
/** The command definition object accepted by {@link defineCommand}. */
|
|
191
|
+
interface DefinedCommand<TArgsFields extends readonly CommandArgField[] = readonly [], TOptionsFields extends readonly CommandOptionField[] = readonly []> {
|
|
192
|
+
readonly description?: string | undefined;
|
|
193
|
+
readonly aliases: readonly string[];
|
|
194
|
+
readonly args: TArgsFields;
|
|
195
|
+
readonly options: TOptionsFields;
|
|
196
|
+
readonly run: (ctx: CommandContext<InferNamedFields<TOptionsFields, true>, InferNamedFields<TArgsFields>>) => void | Promise<void>;
|
|
197
|
+
} //#endregion
|
|
198
|
+
//#region src/define-command.d.ts
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/execute-command.d.ts
|
|
201
|
+
interface ExecuteCommandInput<TOptions, TArgs> {
|
|
202
|
+
readonly options?: TOptions | undefined;
|
|
203
|
+
readonly args?: TArgs | undefined;
|
|
204
|
+
readonly cwd?: string | undefined;
|
|
205
|
+
readonly rawArgs?: readonly string[] | undefined;
|
|
206
|
+
} //#endregion
|
|
207
|
+
//#region src/run-command.d.ts
|
|
4
208
|
type RunCommandOptions<TOptions, TArgs> = ExecuteCommandInput<TOptions, TArgs>;
|
|
5
209
|
interface CommandExecutionResult {
|
|
6
210
|
readonly exitCode: number;
|
|
@@ -8,43 +212,6 @@ interface CommandExecutionResult {
|
|
|
8
212
|
readonly stderr: string;
|
|
9
213
|
readonly errorMessage?: string | undefined;
|
|
10
214
|
}
|
|
11
|
-
|
|
12
|
-
* Runs a command definition directly in-process for testing.
|
|
13
|
-
*
|
|
14
|
-
* This helper bypasses Rune's CLI parser and validation layers. Callers
|
|
15
|
-
* provide already-normalized `options` and `args` values, and the command's
|
|
16
|
-
* `run` function is executed with those values injected into the context.
|
|
17
|
-
*
|
|
18
|
-
* All output written to `process.stdout`, `process.stderr`, and `console` is
|
|
19
|
-
* captured and returned as strings so tests can assert on them.
|
|
20
|
-
*
|
|
21
|
-
* @param command - A command created with {@link defineCommand}.
|
|
22
|
-
* @param options - Pre-validated options, args, cwd, and rawArgs to inject.
|
|
23
|
-
* @returns The exit code, captured stdout/stderr, and an optional error message.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { defineCommand } from "rune";
|
|
28
|
-
* import { runCommand } from "rune/test";
|
|
29
|
-
* import { expect, test } from "vitest";
|
|
30
|
-
*
|
|
31
|
-
* const hello = defineCommand({
|
|
32
|
-
* options: [{ name: "name", type: "string", required: true }],
|
|
33
|
-
* run(ctx) {
|
|
34
|
-
* console.log(`Hello, ${ctx.options.name}!`);
|
|
35
|
-
* },
|
|
36
|
-
* });
|
|
37
|
-
*
|
|
38
|
-
* test("hello command", async () => {
|
|
39
|
-
* const result = await runCommand(hello, {
|
|
40
|
-
* options: { name: "Rune" },
|
|
41
|
-
* });
|
|
42
|
-
*
|
|
43
|
-
* expect(result.exitCode).toBe(0);
|
|
44
|
-
* expect(result.stdout).toBe("Hello, Rune!\n");
|
|
45
|
-
* });
|
|
46
|
-
* ```
|
|
47
|
-
*/
|
|
48
|
-
declare function runCommand<TArgsFields extends readonly CommandArgField[], TOptionsFields extends readonly CommandOptionField[]>(command: DefinedCommand<TArgsFields, TOptionsFields>, options?: RunCommandOptions<InferExecutionFields<TOptionsFields>, InferExecutionFields<TArgsFields>>): Promise<CommandExecutionResult>;
|
|
215
|
+
declare function runCommand<TArgsFields extends readonly CommandArgField[], TOptionsFields extends readonly CommandOptionField[]>(command: DefinedCommand<TArgsFields, TOptionsFields>, options?: RunCommandOptions<InferExecutionFields<TOptionsFields>, InferExecutionFields<TArgsFields>>): Promise<CommandExecutionResult>; //#endregion
|
|
49
216
|
//#endregion
|
|
50
|
-
export { CommandExecutionResult, RunCommandOptions, runCommand };
|
|
217
|
+
export { type CommandExecutionResult, type RunCommandOptions, runCommand };
|