padrone 1.8.2 → 2.0.0
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/CHANGELOG.md +20 -1
- package/README.md +2 -2
- package/dist/codegen/code-builder.d.mts +10 -0
- package/dist/codegen/code-builder.d.mts.map +1 -0
- package/dist/codegen/code-builder.mjs +185 -0
- package/dist/codegen/code-builder.mjs.map +1 -0
- package/dist/codegen/discovery.d.mts +44 -0
- package/dist/codegen/discovery.d.mts.map +1 -0
- package/dist/codegen/discovery.mjs +179 -0
- package/dist/codegen/discovery.mjs.map +1 -0
- package/dist/codegen/file-emitter.d.mts +10 -0
- package/dist/codegen/file-emitter.d.mts.map +1 -0
- package/dist/codegen/file-emitter.mjs +58 -0
- package/dist/codegen/file-emitter.mjs.map +1 -0
- package/dist/codegen/generators/barrel-file.d.mts +10 -0
- package/dist/codegen/generators/barrel-file.d.mts.map +1 -0
- package/dist/codegen/generators/barrel-file.mjs +16 -0
- package/dist/codegen/generators/barrel-file.mjs.map +1 -0
- package/dist/codegen/generators/command-file.d.mts +25 -0
- package/dist/codegen/generators/command-file.d.mts.map +1 -0
- package/dist/codegen/generators/command-file.mjs +130 -0
- package/dist/codegen/generators/command-file.mjs.map +1 -0
- package/dist/codegen/generators/command-tree.d.mts +17 -0
- package/dist/codegen/generators/command-tree.d.mts.map +1 -0
- package/dist/codegen/generators/command-tree.mjs +72 -0
- package/dist/codegen/generators/command-tree.mjs.map +1 -0
- package/dist/codegen/index.d.mts +15 -330
- package/dist/codegen/index.mjs +13 -1507
- package/dist/codegen/parsers/bash.d.mts +19 -0
- package/dist/codegen/parsers/bash.d.mts.map +1 -0
- package/dist/codegen/parsers/bash.mjs +112 -0
- package/dist/codegen/parsers/bash.mjs.map +1 -0
- package/dist/codegen/parsers/fish.d.mts +13 -0
- package/dist/codegen/parsers/fish.d.mts.map +1 -0
- package/dist/codegen/parsers/fish.mjs +108 -0
- package/dist/codegen/parsers/fish.mjs.map +1 -0
- package/dist/codegen/parsers/help.d.mts +15 -0
- package/dist/codegen/parsers/help.d.mts.map +1 -0
- package/dist/codegen/parsers/help.mjs +271 -0
- package/dist/codegen/parsers/help.mjs.map +1 -0
- package/dist/codegen/parsers/merge.d.mts +14 -0
- package/dist/codegen/parsers/merge.d.mts.map +1 -0
- package/dist/codegen/parsers/merge.mjs +76 -0
- package/dist/codegen/parsers/merge.mjs.map +1 -0
- package/dist/codegen/parsers/zsh.d.mts +16 -0
- package/dist/codegen/parsers/zsh.d.mts.map +1 -0
- package/dist/codegen/parsers/zsh.mjs +161 -0
- package/dist/codegen/parsers/zsh.mjs.map +1 -0
- package/dist/codegen/schema-to-code.d.mts +24 -0
- package/dist/codegen/schema-to-code.d.mts.map +1 -0
- package/dist/codegen/schema-to-code.mjs +147 -0
- package/dist/codegen/schema-to-code.mjs.map +1 -0
- package/dist/codegen/template.d.mts +18 -0
- package/dist/codegen/template.d.mts.map +1 -0
- package/dist/codegen/template.mjs +37 -0
- package/dist/codegen/template.mjs.map +1 -0
- package/dist/codegen/types.d.mts +144 -0
- package/dist/codegen/types.d.mts.map +1 -0
- package/dist/completion.d.mts +2 -0
- package/dist/completion.mjs +2 -0
- package/dist/core/args.d.mts +3 -0
- package/dist/{args-DrCXxXeP.mjs → core/args.mjs} +49 -113
- package/dist/core/args.mjs.map +1 -0
- package/dist/core/commands.d.mts +13 -0
- package/dist/core/commands.d.mts.map +1 -0
- package/dist/{commands-DLR0rFgq.mjs → core/commands.mjs} +4 -232
- package/dist/core/commands.mjs.map +1 -0
- package/dist/core/create.d.mts +63 -0
- package/dist/core/create.d.mts.map +1 -0
- package/dist/core/create.mjs +230 -0
- package/dist/core/create.mjs.map +1 -0
- package/dist/core/default-runtime.mjs +198 -0
- package/dist/core/default-runtime.mjs.map +1 -0
- package/dist/core/errors.d.mts +107 -0
- package/dist/core/errors.d.mts.map +1 -0
- package/dist/{errors-DA4KzK1M.mjs → core/errors.mjs} +2 -2
- package/dist/core/errors.mjs.map +1 -0
- package/dist/core/exec.mjs +191 -0
- package/dist/core/exec.mjs.map +1 -0
- package/dist/core/interceptors.d.mts +33 -0
- package/dist/core/interceptors.d.mts.map +1 -0
- package/dist/core/interceptors.mjs +253 -0
- package/dist/core/interceptors.mjs.map +1 -0
- package/dist/core/parse.mjs +181 -0
- package/dist/core/parse.mjs.map +1 -0
- package/dist/core/program-methods.mjs +256 -0
- package/dist/core/program-methods.mjs.map +1 -0
- package/dist/core/results.d.mts +13 -0
- package/dist/core/results.d.mts.map +1 -0
- package/dist/core/results.mjs +150 -0
- package/dist/core/results.mjs.map +1 -0
- package/dist/core/runtime.d.mts +213 -0
- package/dist/core/runtime.d.mts.map +1 -0
- package/dist/core/runtime.mjs +10 -0
- package/dist/core/runtime.mjs.map +1 -0
- package/dist/core/validate.mjs +220 -0
- package/dist/core/validate.mjs.map +1 -0
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/docs/index.mjs.map +1 -1
- package/dist/extension/auto-output.d.mts +53 -0
- package/dist/extension/auto-output.d.mts.map +1 -0
- package/dist/extension/auto-output.mjs +116 -0
- package/dist/extension/auto-output.mjs.map +1 -0
- package/dist/extension/color.d.mts +19 -0
- package/dist/extension/color.d.mts.map +1 -0
- package/dist/extension/color.mjs +37 -0
- package/dist/extension/color.mjs.map +1 -0
- package/dist/extension/completion.d.mts +25 -0
- package/dist/extension/completion.d.mts.map +1 -0
- package/dist/extension/completion.mjs +40 -0
- package/dist/extension/completion.mjs.map +1 -0
- package/dist/extension/config.d.mts +55 -0
- package/dist/extension/config.d.mts.map +1 -0
- package/dist/extension/config.mjs +162 -0
- package/dist/extension/config.mjs.map +1 -0
- package/dist/extension/env.d.mts +42 -0
- package/dist/extension/env.d.mts.map +1 -0
- package/dist/extension/env.mjs +44 -0
- package/dist/extension/env.mjs.map +1 -0
- package/dist/extension/help.d.mts +29 -0
- package/dist/extension/help.d.mts.map +1 -0
- package/dist/extension/help.mjs +161 -0
- package/dist/extension/help.mjs.map +1 -0
- package/dist/extension/index.d.mts +18 -0
- package/dist/extension/ink.d.mts +31 -0
- package/dist/extension/ink.d.mts.map +1 -0
- package/dist/extension/ink.mjs +69 -0
- package/dist/extension/ink.mjs.map +1 -0
- package/dist/extension/interactive.d.mts +16 -0
- package/dist/extension/interactive.d.mts.map +1 -0
- package/dist/extension/interactive.mjs +89 -0
- package/dist/extension/interactive.mjs.map +1 -0
- package/dist/extension/logger.d.mts +67 -0
- package/dist/extension/logger.d.mts.map +1 -0
- package/dist/extension/logger.mjs +188 -0
- package/dist/extension/logger.mjs.map +1 -0
- package/dist/extension/man.d.mts +25 -0
- package/dist/extension/man.d.mts.map +1 -0
- package/dist/extension/man.mjs +41 -0
- package/dist/extension/man.mjs.map +1 -0
- package/dist/extension/mcp.d.mts +28 -0
- package/dist/extension/mcp.d.mts.map +1 -0
- package/dist/extension/mcp.mjs +44 -0
- package/dist/extension/mcp.mjs.map +1 -0
- package/dist/extension/progress-renderer.d.mts +13 -0
- package/dist/extension/progress-renderer.d.mts.map +1 -0
- package/dist/extension/progress-renderer.mjs +351 -0
- package/dist/extension/progress-renderer.mjs.map +1 -0
- package/dist/extension/progress.d.mts +76 -0
- package/dist/extension/progress.d.mts.map +1 -0
- package/dist/extension/progress.mjs +225 -0
- package/dist/extension/progress.mjs.map +1 -0
- package/dist/extension/repl.d.mts +26 -0
- package/dist/extension/repl.d.mts.map +1 -0
- package/dist/extension/repl.mjs +67 -0
- package/dist/extension/repl.mjs.map +1 -0
- package/dist/extension/serve.d.mts +27 -0
- package/dist/extension/serve.d.mts.map +1 -0
- package/dist/extension/serve.mjs +41 -0
- package/dist/extension/serve.mjs.map +1 -0
- package/dist/extension/signal.d.mts +19 -0
- package/dist/extension/signal.d.mts.map +1 -0
- package/dist/extension/signal.mjs +81 -0
- package/dist/extension/signal.mjs.map +1 -0
- package/dist/extension/stdin.d.mts +19 -0
- package/dist/extension/stdin.d.mts.map +1 -0
- package/dist/extension/stdin.mjs +52 -0
- package/dist/extension/stdin.mjs.map +1 -0
- package/dist/extension/suggestions.d.mts +6 -0
- package/dist/extension/suggestions.d.mts.map +1 -0
- package/dist/extension/suggestions.mjs +113 -0
- package/dist/extension/suggestions.mjs.map +1 -0
- package/dist/extension/timing.d.mts +27 -0
- package/dist/extension/timing.d.mts.map +1 -0
- package/dist/extension/timing.mjs +71 -0
- package/dist/extension/timing.mjs.map +1 -0
- package/dist/extension/tracing.d.mts +85 -0
- package/dist/extension/tracing.d.mts.map +1 -0
- package/dist/extension/tracing.mjs +107 -0
- package/dist/extension/tracing.mjs.map +1 -0
- package/dist/extension/update-check.d.mts +20 -0
- package/dist/extension/update-check.d.mts.map +1 -0
- package/dist/extension/update-check.mjs +62 -0
- package/dist/extension/update-check.mjs.map +1 -0
- package/dist/extension/utils.mjs +43 -0
- package/dist/extension/utils.mjs.map +1 -0
- package/dist/extension/version.d.mts +20 -0
- package/dist/extension/version.d.mts.map +1 -0
- package/dist/extension/version.mjs +51 -0
- package/dist/extension/version.mjs.map +1 -0
- package/dist/{completion-UnBKfGuk.mjs → feature/completion.mjs} +4 -3
- package/dist/feature/completion.mjs.map +1 -0
- package/dist/feature/interactive.mjs +145 -0
- package/dist/feature/interactive.mjs.map +1 -0
- package/dist/feature/mcp.d.mts +18 -0
- package/dist/feature/mcp.d.mts.map +1 -0
- package/dist/{mcp-D6PdtjIs.mjs → feature/mcp.mjs} +6 -6
- package/dist/feature/mcp.mjs.map +1 -0
- package/dist/feature/repl-loop.mjs +217 -0
- package/dist/feature/repl-loop.mjs.map +1 -0
- package/dist/feature/serve.d.mts +18 -0
- package/dist/feature/serve.d.mts.map +1 -0
- package/dist/{serve-PaCLsNoD.mjs → feature/serve.mjs} +7 -7
- package/dist/feature/serve.mjs.map +1 -0
- package/dist/feature/test.d.mts +109 -0
- package/dist/feature/test.d.mts.map +1 -0
- package/dist/feature/test.mjs +116 -0
- package/dist/feature/test.mjs.map +1 -0
- package/dist/feature/update-check.d.mts +35 -0
- package/dist/feature/update-check.d.mts.map +1 -0
- package/dist/{update-check-CZ2VqjnV.mjs → feature/update-check.mjs} +2 -2
- package/dist/feature/update-check.mjs.map +1 -0
- package/dist/feature/wrap.d.mts +58 -0
- package/dist/feature/wrap.d.mts.map +1 -0
- package/dist/feature/wrap.mjs +77 -0
- package/dist/feature/wrap.mjs.map +1 -0
- package/dist/index.d.mts +38 -901
- package/dist/index.mjs +25 -4337
- package/dist/ink.d.mts +2 -0
- package/dist/ink.mjs +2 -0
- package/dist/man.d.mts +2 -0
- package/dist/man.mjs +2 -0
- package/dist/mcp.d.mts +3 -0
- package/dist/mcp.mjs +2 -0
- package/dist/output/colorizer.d.mts +28 -0
- package/dist/output/colorizer.d.mts.map +1 -0
- package/dist/output/colorizer.mjs +94 -0
- package/dist/output/colorizer.mjs.map +1 -0
- package/dist/{formatter-CY3KrOEd.d.mts → output/formatter.d.mts} +2 -28
- package/dist/output/formatter.d.mts.map +1 -0
- package/dist/output/formatter.mjs +360 -0
- package/dist/output/formatter.mjs.map +1 -0
- package/dist/output/help.d.mts +18 -0
- package/dist/output/help.d.mts.map +1 -0
- package/dist/output/help.mjs +267 -0
- package/dist/output/help.mjs.map +1 -0
- package/dist/output/output-indicator.d.mts +27 -0
- package/dist/output/output-indicator.d.mts.map +1 -0
- package/dist/output/output-indicator.mjs +54 -0
- package/dist/output/output-indicator.mjs.map +1 -0
- package/dist/output/primitives.d.mts +33 -0
- package/dist/output/primitives.d.mts.map +1 -0
- package/dist/output/primitives.mjs +206 -0
- package/dist/output/primitives.mjs.map +1 -0
- package/dist/output/styling.d.mts +38 -0
- package/dist/output/styling.d.mts.map +1 -0
- package/dist/output/styling.mjs +146 -0
- package/dist/output/styling.mjs.map +1 -0
- package/dist/schema/zod.d.mts +6 -0
- package/dist/schema/zod.d.mts.map +1 -0
- package/dist/schema/zod.mjs +50 -0
- package/dist/schema/zod.mjs.map +1 -0
- package/dist/serve.d.mts +2 -0
- package/dist/serve.mjs +2 -0
- package/dist/test.d.mts +2 -109
- package/dist/test.mjs +1 -115
- package/dist/tracing.d.mts +2 -0
- package/dist/tracing.mjs +2 -0
- package/dist/types/args-meta.d.mts +129 -0
- package/dist/types/args-meta.d.mts.map +1 -0
- package/dist/types/builder.d.mts +212 -0
- package/dist/types/builder.d.mts.map +1 -0
- package/dist/types/command.d.mts +112 -0
- package/dist/types/command.d.mts.map +1 -0
- package/dist/types/index.d.mts +8 -0
- package/dist/types/interceptor.d.mts +232 -0
- package/dist/types/interceptor.d.mts.map +1 -0
- package/dist/types/preferences.d.mts +79 -0
- package/dist/types/preferences.d.mts.map +1 -0
- package/dist/types/result.d.mts +61 -0
- package/dist/types/result.d.mts.map +1 -0
- package/dist/types/schema.d.mts +24 -0
- package/dist/types/schema.d.mts.map +1 -0
- package/dist/util/dotenv.mjs +174 -0
- package/dist/util/dotenv.mjs.map +1 -0
- package/dist/util/shell-utils.mjs +80 -0
- package/dist/util/shell-utils.mjs.map +1 -0
- package/dist/util/stream.d.mts +33 -0
- package/dist/util/stream.d.mts.map +1 -0
- package/dist/{stream-DC4H8YTx.mjs → util/stream.mjs} +2 -2
- package/dist/util/stream.mjs.map +1 -0
- package/dist/util/type-helpers.d.mts +74 -0
- package/dist/util/type-helpers.d.mts.map +1 -0
- package/dist/util/type-utils.d.mts +186 -0
- package/dist/util/type-utils.d.mts.map +1 -0
- package/dist/util/utils.mjs +43 -0
- package/dist/util/utils.mjs.map +1 -0
- package/dist/zod.d.mts +1 -32
- package/dist/zod.mjs +1 -49
- package/package.json +65 -17
- package/src/completion.ts +2 -0
- package/src/core/args.ts +63 -37
- package/src/core/runtime.ts +11 -2
- package/src/extension/completion.ts +3 -0
- package/src/extension/index.ts +0 -12
- package/src/extension/ink.ts +2 -1
- package/src/extension/man.ts +3 -0
- package/src/extension/mcp.ts +3 -0
- package/src/extension/progress-renderer.ts +32 -8
- package/src/extension/progress.ts +19 -9
- package/src/extension/serve.ts +3 -0
- package/src/extension/tracing.ts +2 -0
- package/src/index.ts +1 -20
- package/src/ink.ts +2 -0
- package/src/man.ts +2 -0
- package/src/mcp.ts +3 -0
- package/src/serve.ts +2 -0
- package/src/tracing.ts +9 -0
- package/src/types/builder.ts +2 -2
- package/src/zod.ts +1 -1
- package/dist/args-DrCXxXeP.mjs.map +0 -1
- package/dist/codegen/index.d.mts.map +0 -1
- package/dist/codegen/index.mjs.map +0 -1
- package/dist/commands-DLR0rFgq.mjs.map +0 -1
- package/dist/completion-UnBKfGuk.mjs.map +0 -1
- package/dist/errors-DA4KzK1M.mjs.map +0 -1
- package/dist/formatter-CY3KrOEd.d.mts.map +0 -1
- package/dist/help-B-ZMYyn-.mjs +0 -892
- package/dist/help-B-ZMYyn-.mjs.map +0 -1
- package/dist/index-D-Dpz7l_.d.mts +0 -1427
- package/dist/index-D-Dpz7l_.d.mts.map +0 -1
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/mcp-D6PdtjIs.mjs.map +0 -1
- package/dist/serve-PaCLsNoD.mjs.map +0 -1
- package/dist/stream-DC4H8YTx.mjs.map +0 -1
- package/dist/test.d.mts.map +0 -1
- package/dist/test.mjs.map +0 -1
- package/dist/update-check-CZ2VqjnV.mjs.map +0 -1
- package/dist/zod.d.mts.map +0 -1
- package/dist/zod.mjs.map +0 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { getCommandRuntime } from "./commands.mjs";
|
|
2
|
+
//#region src/core/results.ts
|
|
3
|
+
/**
|
|
4
|
+
* Brands a schema as async, signaling that its `validate()` may return a Promise.
|
|
5
|
+
* When an async-branded schema is passed to `.arguments()`, `.configFile()`, or `.env()`,
|
|
6
|
+
* the command's `parse()` and `cli()` will return Promises.
|
|
7
|
+
*/
|
|
8
|
+
function asyncSchema(schema) {
|
|
9
|
+
return Object.assign(schema, { "~async": true });
|
|
10
|
+
}
|
|
11
|
+
const noop = () => void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Maps over a value that may or may not be a Promise.
|
|
14
|
+
* If the value is a Promise, chains with `.then()`. Otherwise, calls the function synchronously.
|
|
15
|
+
* This preserves sync behavior for sync schemas and async behavior for async schemas.
|
|
16
|
+
*/
|
|
17
|
+
function thenMaybe(value, fn) {
|
|
18
|
+
if (value instanceof Promise) return value.then(fn);
|
|
19
|
+
return fn(value);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Makes a sync result object thenable by adding `.then()`, `.catch()`, and `.finally()` methods.
|
|
23
|
+
* If the value is already a Promise, returns it as-is.
|
|
24
|
+
* This allows users to write `await program.cli()` or `program.cli().then(...)` regardless of sync/async.
|
|
25
|
+
*
|
|
26
|
+
* The `.then()` resolves with a plain copy (without thenable methods) to avoid infinite
|
|
27
|
+
* recursive unwrapping by the Promise resolution algorithm.
|
|
28
|
+
*/
|
|
29
|
+
function makeThenable(value) {
|
|
30
|
+
if (value instanceof Promise) return value;
|
|
31
|
+
if (value !== null && typeof value === "object" && !("then" in value)) {
|
|
32
|
+
const toPlain = () => {
|
|
33
|
+
const plain = { ...value };
|
|
34
|
+
delete plain.then;
|
|
35
|
+
delete plain.catch;
|
|
36
|
+
delete plain.finally;
|
|
37
|
+
return plain;
|
|
38
|
+
};
|
|
39
|
+
value.then = (onfulfilled, onrejected) => {
|
|
40
|
+
try {
|
|
41
|
+
const result = onfulfilled ? onfulfilled(toPlain()) : toPlain();
|
|
42
|
+
return Promise.resolve(result);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (onrejected) return Promise.resolve(onrejected(err));
|
|
45
|
+
return Promise.reject(err);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
value.catch = (onrejected) => value.then(void 0, onrejected);
|
|
49
|
+
value.finally = (onfinally) => value.then((v) => {
|
|
50
|
+
onfinally?.();
|
|
51
|
+
return v;
|
|
52
|
+
}, (err) => {
|
|
53
|
+
onfinally?.();
|
|
54
|
+
throw err;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Wraps a Promise to include a `drain()` method at the top level.
|
|
61
|
+
* This allows `await promise.drain()` without first awaiting the promise.
|
|
62
|
+
* Since cli/eval never reject, this just delegates to the resolved result's `drain()`.
|
|
63
|
+
*/
|
|
64
|
+
function withPromiseDrain(promise) {
|
|
65
|
+
promise.drain = async () => {
|
|
66
|
+
return (await promise).drain();
|
|
67
|
+
};
|
|
68
|
+
return promise;
|
|
69
|
+
}
|
|
70
|
+
function isIterator(value) {
|
|
71
|
+
return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
|
|
72
|
+
}
|
|
73
|
+
function isAsyncIterator(value) {
|
|
74
|
+
return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolves a result value by unwrapping Promises and collecting iterables into arrays.
|
|
78
|
+
* This is the runtime counterpart of the `Drained<T>` type.
|
|
79
|
+
*/
|
|
80
|
+
async function drainValue(value) {
|
|
81
|
+
if (value instanceof Promise) return drainValue(await value);
|
|
82
|
+
if (isAsyncIterator(value)) {
|
|
83
|
+
const items = [];
|
|
84
|
+
const iter = value[Symbol.asyncIterator]();
|
|
85
|
+
while (true) {
|
|
86
|
+
const { done, value: item } = await iter.next();
|
|
87
|
+
if (done) break;
|
|
88
|
+
items.push(item);
|
|
89
|
+
}
|
|
90
|
+
return items;
|
|
91
|
+
}
|
|
92
|
+
if (typeof value !== "string" && !Array.isArray(value) && isIterator(value)) {
|
|
93
|
+
const items = [];
|
|
94
|
+
const iter = value[Symbol.iterator]();
|
|
95
|
+
while (true) {
|
|
96
|
+
const { done, value: item } = iter.next();
|
|
97
|
+
if (done) break;
|
|
98
|
+
items.push(item);
|
|
99
|
+
}
|
|
100
|
+
return items;
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Attaches a `drain()` method to a command result object.
|
|
106
|
+
* If the result has an `error` field, `drain()` returns `{ error }`.
|
|
107
|
+
* Otherwise, resolves the result (unwrapping Promises, collecting iterables), catches errors,
|
|
108
|
+
* and returns a discriminated union `{ value } | { error }` that never throws.
|
|
109
|
+
*/
|
|
110
|
+
function withDrain(obj) {
|
|
111
|
+
obj.drain = async () => {
|
|
112
|
+
if ("error" in obj && obj.error !== void 0) return { error: obj.error };
|
|
113
|
+
try {
|
|
114
|
+
return { value: await drainValue(obj.result) };
|
|
115
|
+
} catch (err) {
|
|
116
|
+
return { error: err };
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
return obj;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Creates an error command result with a `drain()` that returns the error.
|
|
123
|
+
*/
|
|
124
|
+
function errorResult(error, partial) {
|
|
125
|
+
return withDrain({
|
|
126
|
+
error,
|
|
127
|
+
result: void 0,
|
|
128
|
+
command: partial?.command,
|
|
129
|
+
args: partial?.args,
|
|
130
|
+
argsResult: partial?.argsResult
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function isAsyncBranded(schema) {
|
|
134
|
+
return !!schema && typeof schema === "object" && "~async" in schema && schema["~async"] === true;
|
|
135
|
+
}
|
|
136
|
+
function hasInteractiveConfig(meta) {
|
|
137
|
+
if (!meta || typeof meta !== "object") return false;
|
|
138
|
+
const m = meta;
|
|
139
|
+
return m.interactive === true || Array.isArray(m.interactive) || m.optionalInteractive === true || Array.isArray(m.optionalInteractive);
|
|
140
|
+
}
|
|
141
|
+
function warnIfUnexpectedAsync(value, command) {
|
|
142
|
+
const runtime = getCommandRuntime(command);
|
|
143
|
+
if (runtime.env().NODE_ENV === "production") return value;
|
|
144
|
+
if (value instanceof Promise && !command.isAsync) runtime.error(`[padrone] Command "${command.path || command.name}" returned a Promise from validation, but was not marked as async. Use \`.async()\` on the builder or \`asyncSchema()\` to brand your schema. Without this, TypeScript will infer a sync return type and the result will be a Promise at runtime.`);
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { asyncSchema, drainValue, errorResult, hasInteractiveConfig, isAsyncBranded, isAsyncIterator, isIterator, makeThenable, noop, thenMaybe, warnIfUnexpectedAsync, withDrain, withPromiseDrain };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=results.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.mjs","names":[],"sources":["../../src/core/results.ts"],"sourcesContent":["import type { AnyPadroneCommand, PadroneSchema } from '../types/index.ts';\nimport type { Thenable } from '../util/type-utils.ts';\nimport { getCommandRuntime } from './commands.ts';\n\n/**\n * Brands a schema as async, signaling that its `validate()` may return a Promise.\n * When an async-branded schema is passed to `.arguments()`, `.configFile()`, or `.env()`,\n * the command's `parse()` and `cli()` will return Promises.\n */\nexport function asyncSchema<T extends PadroneSchema>(schema: T): T & { '~async': true } {\n return Object.assign(schema, { '~async': true as const });\n}\n\nexport const noop = <TRes>() => undefined as TRes;\n\n/**\n * Maps over a value that may or may not be a Promise.\n * If the value is a Promise, chains with `.then()`. Otherwise, calls the function synchronously.\n * This preserves sync behavior for sync schemas and async behavior for async schemas.\n */\nexport function thenMaybe<T, U>(value: T | Promise<T>, fn: (v: T) => U | Promise<U>): U | Promise<U> {\n if (value instanceof Promise) return value.then(fn);\n return fn(value);\n}\n\n/**\n * Makes a sync result object thenable by adding `.then()`, `.catch()`, and `.finally()` methods.\n * If the value is already a Promise, returns it as-is.\n * This allows users to write `await program.cli()` or `program.cli().then(...)` regardless of sync/async.\n *\n * The `.then()` resolves with a plain copy (without thenable methods) to avoid infinite\n * recursive unwrapping by the Promise resolution algorithm.\n */\nexport function makeThenable<T>(value: T | Promise<T>): Thenable<T> {\n if (value instanceof Promise) return value as any;\n if (value !== null && typeof value === 'object' && !('then' in value)) {\n const toPlain = () => {\n const plain = { ...value } as any;\n delete plain.then;\n delete plain.catch;\n delete plain.finally;\n return plain as T;\n };\n // biome-ignore lint/suspicious/noThenProperty: intentional thenable shim for sync results\n (value as any).then = (onfulfilled?: (v: T) => any, onrejected?: (reason: any) => any) => {\n try {\n const result = onfulfilled ? onfulfilled(toPlain()) : toPlain();\n return Promise.resolve(result);\n } catch (err) {\n if (onrejected) return Promise.resolve(onrejected(err));\n return Promise.reject(err);\n }\n };\n (value as any).catch = (onrejected?: (reason: any) => any) => (value as any).then(undefined, onrejected);\n (value as any).finally = (onfinally?: () => void) =>\n (value as any).then(\n (v: any) => {\n onfinally?.();\n return v;\n },\n (err: any) => {\n onfinally?.();\n throw err;\n },\n );\n }\n return value as any;\n}\n\n/**\n * Wraps a Promise to include a `drain()` method at the top level.\n * This allows `await promise.drain()` without first awaiting the promise.\n * Since cli/eval never reject, this just delegates to the resolved result's `drain()`.\n */\nexport function withPromiseDrain<T extends Promise<any>>(promise: T): T & { drain: () => Promise<any> } {\n (promise as any).drain = async () => {\n const resolved = await promise;\n return resolved.drain();\n };\n return promise as any;\n}\n\nexport function isIterator(value: unknown): value is Iterator<unknown> {\n return typeof value === 'object' && value !== null && Symbol.iterator in value && typeof (value as any)[Symbol.iterator] === 'function';\n}\n\nexport function isAsyncIterator(value: unknown): value is AsyncIterator<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n Symbol.asyncIterator in value &&\n typeof (value as any)[Symbol.asyncIterator] === 'function'\n );\n}\n\n/**\n * Writes a command's return value to output, handling promises, iterators, and async iterators.\n * Values are passed directly to the output function without stringification —\n * runtimes like Node/Bun already format objects via console.log.\n * Returns void or a Promise depending on whether async consumption is needed.\n */\nexport function outputValue(value: unknown, output: (...args: unknown[]) => void): void | Promise<void> {\n if (value == null) return;\n\n // Async iterator — consume and output each yielded value\n if (isAsyncIterator(value)) {\n return (async () => {\n const iter = (value as any)[Symbol.asyncIterator]();\n while (true) {\n const { done, value: item } = await iter.next();\n if (done) break;\n if (item != null) output(item);\n }\n })();\n }\n\n // Sync iterator (but not a plain string/array which also have Symbol.iterator)\n if (typeof value !== 'string' && !Array.isArray(value) && isIterator(value)) {\n const iter = (value as any)[Symbol.iterator]();\n while (true) {\n const { done, value: item } = iter.next();\n if (done) break;\n if (item != null) output(item);\n }\n return;\n }\n\n // Promise — await then output\n if (value instanceof Promise) {\n return value.then((resolved) => outputValue(resolved, output));\n }\n\n // Pass value directly — runtime handles formatting\n output(value);\n}\n\n/**\n * Resolves a result value by unwrapping Promises and collecting iterables into arrays.\n * This is the runtime counterpart of the `Drained<T>` type.\n */\nexport async function drainValue(value: unknown): Promise<unknown> {\n // Unwrap promises first\n if (value instanceof Promise) {\n return drainValue(await value);\n }\n\n // Async iterator — collect into array\n if (isAsyncIterator(value)) {\n const items: unknown[] = [];\n const iter = (value as any)[Symbol.asyncIterator]();\n while (true) {\n const { done, value: item } = await iter.next();\n if (done) break;\n items.push(item);\n }\n return items;\n }\n\n // Sync iterator (but not string/array)\n if (typeof value !== 'string' && !Array.isArray(value) && isIterator(value)) {\n const items: unknown[] = [];\n const iter = (value as any)[Symbol.iterator]();\n while (true) {\n const { done, value: item } = iter.next();\n if (done) break;\n items.push(item);\n }\n return items;\n }\n\n return value;\n}\n\n/**\n * Attaches a `drain()` method to a command result object.\n * If the result has an `error` field, `drain()` returns `{ error }`.\n * Otherwise, resolves the result (unwrapping Promises, collecting iterables), catches errors,\n * and returns a discriminated union `{ value } | { error }` that never throws.\n */\nexport function withDrain<T extends Record<string, unknown>>(obj: T): T & { drain: () => Promise<any> } {\n (obj as any).drain = async () => {\n if ('error' in obj && obj.error !== undefined) {\n return { error: obj.error };\n }\n try {\n const value = await drainValue(obj.result);\n return { value };\n } catch (err) {\n return { error: err };\n }\n };\n return obj as any;\n}\n\n/**\n * Creates an error command result with a `drain()` that returns the error.\n */\nexport function errorResult(error: unknown, partial?: { command?: unknown; args?: unknown; argsResult?: unknown }) {\n return withDrain({\n error,\n result: undefined,\n command: partial?.command,\n args: partial?.args,\n argsResult: partial?.argsResult,\n });\n}\n\nexport function isAsyncBranded(schema: unknown): boolean {\n return !!schema && typeof schema === 'object' && '~async' in schema && (schema as any)['~async'] === true;\n}\n\nexport function hasInteractiveConfig(meta: unknown): boolean {\n if (!meta || typeof meta !== 'object') return false;\n const m = meta as Record<string, unknown>;\n return m.interactive === true || Array.isArray(m.interactive) || m.optionalInteractive === true || Array.isArray(m.optionalInteractive);\n}\n\nexport function warnIfUnexpectedAsync<T>(value: T, command: AnyPadroneCommand): T {\n const runtime = getCommandRuntime(command);\n if (runtime.env().NODE_ENV === 'production') return value;\n if (value instanceof Promise && !command.isAsync) {\n runtime.error(\n `[padrone] Command \"${command.path || command.name}\" returned a Promise from validation, ` +\n `but was not marked as async. Use \\`.async()\\` on the builder or \\`asyncSchema()\\` to brand your schema. ` +\n `Without this, TypeScript will infer a sync return type and the result will be a Promise at runtime.`,\n );\n }\n return value;\n}\n"],"mappings":";;;;;;;AASA,SAAgB,YAAqC,QAAmC;CACtF,OAAO,OAAO,OAAO,QAAQ,EAAE,UAAU,KAAc,CAAC;AAC1D;AAEA,MAAa,aAAmB,KAAA;;;;;;AAOhC,SAAgB,UAAgB,OAAuB,IAA8C;CACnG,IAAI,iBAAiB,SAAS,OAAO,MAAM,KAAK,EAAE;CAClD,OAAO,GAAG,KAAK;AACjB;;;;;;;;;AAUA,SAAgB,aAAgB,OAAoC;CAClE,IAAI,iBAAiB,SAAS,OAAO;CACrC,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,EAAE,UAAU,QAAQ;EACrE,MAAM,gBAAgB;GACpB,MAAM,QAAQ,EAAE,GAAG,MAAM;GACzB,OAAO,MAAM;GACb,OAAO,MAAM;GACb,OAAO,MAAM;GACb,OAAO;EACT;EAEA,MAAe,QAAQ,aAA6B,eAAsC;GACxF,IAAI;IACF,MAAM,SAAS,cAAc,YAAY,QAAQ,CAAC,IAAI,QAAQ;IAC9D,OAAO,QAAQ,QAAQ,MAAM;GAC/B,SAAS,KAAK;IACZ,IAAI,YAAY,OAAO,QAAQ,QAAQ,WAAW,GAAG,CAAC;IACtD,OAAO,QAAQ,OAAO,GAAG;GAC3B;EACF;EACA,MAAe,SAAS,eAAuC,MAAc,KAAK,KAAA,GAAW,UAAU;EACvG,MAAe,WAAW,cACvB,MAAc,MACZ,MAAW;GACV,YAAY;GACZ,OAAO;EACT,IACC,QAAa;GACZ,YAAY;GACZ,MAAM;EACR,CACF;CACJ;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,iBAAyC,SAA+C;CACtG,QAAiB,QAAQ,YAAY;EAEnC,QAAO,MADgB,QAAA,CACP,MAAM;CACxB;CACA,OAAO;AACT;AAEA,SAAgB,WAAW,OAA4C;CACrE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAO,YAAY,SAAS,OAAQ,MAAc,OAAO,cAAc;AAC/H;AAEA,SAAgB,gBAAgB,OAAiD;CAC/E,OACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAO,iBAAiB,SACxB,OAAQ,MAAc,OAAO,mBAAmB;AAEpD;;;;;AA+CA,eAAsB,WAAW,OAAkC;CAEjE,IAAI,iBAAiB,SACnB,OAAO,WAAW,MAAM,KAAK;CAI/B,IAAI,gBAAgB,KAAK,GAAG;EAC1B,MAAM,QAAmB,CAAC;EAC1B,MAAM,OAAQ,MAAc,OAAO,cAAc,CAAC;EAClD,OAAO,MAAM;GACX,MAAM,EAAE,MAAM,OAAO,SAAS,MAAM,KAAK,KAAK;GAC9C,IAAI,MAAM;GACV,MAAM,KAAK,IAAI;EACjB;EACA,OAAO;CACT;CAGA,IAAI,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,KAAK,WAAW,KAAK,GAAG;EAC3E,MAAM,QAAmB,CAAC;EAC1B,MAAM,OAAQ,MAAc,OAAO,SAAS,CAAC;EAC7C,OAAO,MAAM;GACX,MAAM,EAAE,MAAM,OAAO,SAAS,KAAK,KAAK;GACxC,IAAI,MAAM;GACV,MAAM,KAAK,IAAI;EACjB;EACA,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,UAA6C,KAA2C;CACtG,IAAa,QAAQ,YAAY;EAC/B,IAAI,WAAW,OAAO,IAAI,UAAU,KAAA,GAClC,OAAO,EAAE,OAAO,IAAI,MAAM;EAE5B,IAAI;GAEF,OAAO,EAAE,OAAA,MADW,WAAW,IAAI,MAAM,EAC1B;EACjB,SAAS,KAAK;GACZ,OAAO,EAAE,OAAO,IAAI;EACtB;CACF;CACA,OAAO;AACT;;;;AAKA,SAAgB,YAAY,OAAgB,SAAuE;CACjH,OAAO,UAAU;EACf;EACA,QAAQ,KAAA;EACR,SAAS,SAAS;EAClB,MAAM,SAAS;EACf,YAAY,SAAS;CACvB,CAAC;AACH;AAEA,SAAgB,eAAe,QAA0B;CACvD,OAAO,CAAC,CAAC,UAAU,OAAO,WAAW,YAAY,YAAY,UAAW,OAAe,cAAc;AACvG;AAEA,SAAgB,qBAAqB,MAAwB;CAC3D,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,IAAI;CACV,OAAO,EAAE,gBAAgB,QAAQ,MAAM,QAAQ,EAAE,WAAW,KAAK,EAAE,wBAAwB,QAAQ,MAAM,QAAQ,EAAE,mBAAmB;AACxI;AAEA,SAAgB,sBAAyB,OAAU,SAA+B;CAChF,MAAM,UAAU,kBAAkB,OAAO;CACzC,IAAI,QAAQ,IAAI,CAAC,CAAC,aAAa,cAAc,OAAO;CACpD,IAAI,iBAAiB,WAAW,CAAC,QAAQ,SACvC,QAAQ,MACN,sBAAsB,QAAQ,QAAQ,QAAQ,KAAK,kPAGrD;CAEF,OAAO;AACT"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { ColorConfig, ColorTheme } from "../output/colorizer.mjs";
|
|
2
|
+
import { HelpFormat } from "../output/formatter.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/core/runtime.d.ts
|
|
5
|
+
/** Process signals that Padrone can handle for graceful shutdown. */
|
|
6
|
+
type PadroneSignal = 'SIGINT' | 'SIGTERM' | 'SIGHUP';
|
|
7
|
+
/** Value accepted by `PadroneProgress.update()`. */
|
|
8
|
+
type PadroneProgressUpdate = string | number | {
|
|
9
|
+
message?: string;
|
|
10
|
+
progress?: number;
|
|
11
|
+
indeterminate?: boolean;
|
|
12
|
+
time?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A progress indicator instance (spinner, progress bar, etc).
|
|
16
|
+
* Created by the runtime's `progress` factory and used to show loading state during command execution.
|
|
17
|
+
*/
|
|
18
|
+
type PadroneProgress = {
|
|
19
|
+
/**
|
|
20
|
+
* Update the indicator.
|
|
21
|
+
* - `string` — update the displayed message.
|
|
22
|
+
* - `number` — set progress ratio (0–1). Values outside this range are clamped.
|
|
23
|
+
* - `{ message?, progress?, indeterminate? }` — update message, progress, or both.
|
|
24
|
+
*
|
|
25
|
+
* Set `indeterminate: true` to force the bar into indeterminate mode (shows animation, no percentage).
|
|
26
|
+
* This makes the bar visible even in `show: 'auto'` mode without providing a number.
|
|
27
|
+
* Omitting `progress` (or passing a string) leaves the bar in its current state.
|
|
28
|
+
* Setting `progress` when bar mode is not enabled is a no-op for the bar portion.
|
|
29
|
+
*
|
|
30
|
+
* Set `time: true` to start the elapsed timer on demand (useful when `time` was not set in options).
|
|
31
|
+
* Set `time: false` to hide the elapsed timer.
|
|
32
|
+
*/
|
|
33
|
+
update: (value: PadroneProgressUpdate) => void; /** Mark as succeeded and stop. Pass `null` to stop without rendering a final message. */
|
|
34
|
+
succeed: (message?: string | null, options?: {
|
|
35
|
+
indicator?: string;
|
|
36
|
+
}) => void; /** Mark as failed and stop. Pass `null` to stop without rendering a final message. */
|
|
37
|
+
fail: (message?: string | null, options?: {
|
|
38
|
+
indicator?: string;
|
|
39
|
+
}) => void; /** Control ETA (estimated time remaining) display at runtime. */
|
|
40
|
+
eta: {
|
|
41
|
+
/** Enable ETA tracking. Starts collecting samples from subsequent `update()` calls. */start: () => void; /** Disable ETA display. */
|
|
42
|
+
stop: () => void; /** Clear collected samples and restart estimation. Useful between operation phases. */
|
|
43
|
+
reset: () => void;
|
|
44
|
+
}; /** Stop without success/fail status. */
|
|
45
|
+
stop: () => void; /** Temporarily hide the indicator so other output can be written cleanly. */
|
|
46
|
+
pause: () => void; /** Redraw the indicator after a `pause()`. */
|
|
47
|
+
resume: () => void;
|
|
48
|
+
};
|
|
49
|
+
/** Controls when a progress element (spinner or bar) is visible. */
|
|
50
|
+
type PadroneProgressShow = 'auto' | 'always' | 'never';
|
|
51
|
+
/** Built-in spinner presets. */
|
|
52
|
+
type PadroneSpinnerPreset = 'dots' | 'line' | 'arc' | 'bounce';
|
|
53
|
+
/**
|
|
54
|
+
* Spinner configuration for progress indicators.
|
|
55
|
+
* - A preset name (e.g., `'dots'`) to use built-in frames.
|
|
56
|
+
* - `true` — default spinner with `show: 'always'` (visible even alongside a bar).
|
|
57
|
+
* - An object with custom `frames`, `interval`, and/or `show`.
|
|
58
|
+
* - `false` to disable the spinner (`show: 'never'`).
|
|
59
|
+
*
|
|
60
|
+
* Default `show` is `'auto'`: visible when the bar is not shown.
|
|
61
|
+
*/
|
|
62
|
+
type PadroneSpinnerConfig = PadroneSpinnerPreset | boolean | {
|
|
63
|
+
frames?: string[];
|
|
64
|
+
interval?: number;
|
|
65
|
+
show?: PadroneProgressShow;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Options passed to the runtime's `progress` factory.
|
|
69
|
+
*/
|
|
70
|
+
/** Common fill/empty character pairs for progress bars. */
|
|
71
|
+
type PadroneBarChar = '█' | '░' | '▓' | '▒' | '─' | '━' | '■' | '□' | '#' | '-' | '=' | '·' | '▰' | '▱' | (string & {});
|
|
72
|
+
/**
|
|
73
|
+
* Built-in indeterminate bar animation presets.
|
|
74
|
+
* - `'bounce'` — a filled segment slides back and forth (default).
|
|
75
|
+
* - `'slide'` — a filled segment slides left-to-right and wraps around.
|
|
76
|
+
* - `'pulse'` — the entire bar fades in and out using gradient characters (`░▒▓█▓▒░`).
|
|
77
|
+
*/
|
|
78
|
+
type PadroneBarAnimation = 'bounce' | 'slide' | 'pulse';
|
|
79
|
+
/**
|
|
80
|
+
* Progress bar configuration.
|
|
81
|
+
*/
|
|
82
|
+
type PadroneBarConfig = {
|
|
83
|
+
/** Total width of the bar in characters. Defaults to `20`. */width?: number; /** Character used for the filled portion of the bar. Defaults to `'█'`. */
|
|
84
|
+
filled?: PadroneBarChar; /** Character used for the empty portion of the bar. Defaults to `'░'`. */
|
|
85
|
+
empty?: PadroneBarChar; /** Indeterminate animation style. Defaults to `'bounce'`. */
|
|
86
|
+
animation?: PadroneBarAnimation;
|
|
87
|
+
/**
|
|
88
|
+
* When the bar is visible. Defaults to `'always'` when bar is enabled, `'auto'` when bar is not explicitly configured.
|
|
89
|
+
* - `'always'` — bar is always shown (indeterminate until a number is provided).
|
|
90
|
+
* - `'auto'` — bar is shown only after `update()` is called with a number.
|
|
91
|
+
* - `'never'` — bar is never shown.
|
|
92
|
+
*/
|
|
93
|
+
show?: PadroneProgressShow;
|
|
94
|
+
};
|
|
95
|
+
type PadroneProgressOptions = {
|
|
96
|
+
spinner?: PadroneSpinnerConfig; /** Enable a progress bar. `true` for defaults (`show: 'always'`), or a `PadroneBarConfig` object. `false` to disable entirely. When omitted, bar defaults to `show: 'auto'` (appears when a number is provided). */
|
|
97
|
+
bar?: boolean | PadroneBarConfig; /** Show elapsed time since the indicator started. Defaults to `false`. */
|
|
98
|
+
time?: boolean; /** Show estimated time remaining based on progress rate. Requires numeric `update()` calls. Defaults to `false`. */
|
|
99
|
+
eta?: boolean; /** Character/string shown before the success message. Defaults to `'✔'`. */
|
|
100
|
+
successIndicator?: string; /** Character/string shown before the error message. Defaults to `'✖'`. */
|
|
101
|
+
errorIndicator?: string;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Controls interactive prompting capability and default behavior at the runtime level.
|
|
105
|
+
* - `'supported'` — capable; caller decides.
|
|
106
|
+
* - `'unsupported'` — hard veto; nothing can override.
|
|
107
|
+
* - `'forced'` — capable and forces prompts by default.
|
|
108
|
+
* - `'disabled'` — capable but suppresses prompts by default.
|
|
109
|
+
*/
|
|
110
|
+
type InteractiveMode = 'supported' | 'unsupported' | 'forced' | 'disabled';
|
|
111
|
+
/**
|
|
112
|
+
* Configuration passed to the runtime's `prompt` function for interactive field prompting.
|
|
113
|
+
* The prompt type and choices are auto-detected from the field's JSON schema.
|
|
114
|
+
*/
|
|
115
|
+
type InteractivePromptConfig = {
|
|
116
|
+
/** The field name being prompted. */name: string; /** Human-readable message/label for the prompt, derived from the field's description or name. */
|
|
117
|
+
message: string; /** The prompt type, auto-detected from the JSON schema. */
|
|
118
|
+
type: 'input' | 'confirm' | 'select' | 'multiselect' | 'password'; /** Available choices for select/multiselect prompts. */
|
|
119
|
+
choices?: {
|
|
120
|
+
label: string;
|
|
121
|
+
value: unknown;
|
|
122
|
+
}[]; /** Default value from the schema. */
|
|
123
|
+
default?: unknown;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Defines the execution context for a Padrone program.
|
|
127
|
+
* Abstracts all environment-dependent I/O so the CLI framework
|
|
128
|
+
* can run outside of a terminal (e.g., web UIs, chat interfaces, testing).
|
|
129
|
+
*
|
|
130
|
+
* All fields are optional — unspecified fields fall back to the Node.js/Bun defaults.
|
|
131
|
+
*/
|
|
132
|
+
type PadroneRuntime = {
|
|
133
|
+
/** Write normal output (replaces console.log). Receives the raw value — runtime handles formatting. */output?: (...args: unknown[]) => void; /** Write error output (replaces console.error). */
|
|
134
|
+
error?: (text: string) => void; /** Return the raw CLI arguments (replaces process.argv.slice(2)). */
|
|
135
|
+
argv?: () => string[]; /** Return environment variables (replaces process.env). */
|
|
136
|
+
env?: () => Record<string, string | undefined>; /** Default help output format. */
|
|
137
|
+
format?: HelpFormat | 'auto'; /** Color theme for ANSI/console help output. A theme name or partial color config. */
|
|
138
|
+
theme?: ColorTheme | ColorConfig;
|
|
139
|
+
/**
|
|
140
|
+
* Standard input abstraction. Provides methods to read piped data from stdin.
|
|
141
|
+
* When not provided, defaults to reading from `process.stdin`.
|
|
142
|
+
*
|
|
143
|
+
* Used by commands that declare a `stdin` field in their arguments meta.
|
|
144
|
+
* The framework reads stdin automatically during the validate phase and
|
|
145
|
+
* injects the data into the specified argument field.
|
|
146
|
+
*/
|
|
147
|
+
stdin?: {
|
|
148
|
+
/** Whether stdin is a TTY (interactive terminal) vs a pipe/file. */isTTY?: boolean; /** Read all of stdin as a string. */
|
|
149
|
+
text: () => Promise<string>; /** Async iterable of lines for streaming. */
|
|
150
|
+
lines: () => AsyncIterable<string>;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Controls interactive prompting capability and default behavior.
|
|
154
|
+
* - `'supported'` — runtime can handle prompts; caller (flag/pref) decides whether to prompt. This is the default when `prompt` is provided.
|
|
155
|
+
* - `'unsupported'` — runtime cannot handle prompts; hard veto that nothing can override.
|
|
156
|
+
* - `'forced'` — runtime supports prompts and forces them by default (prompts even for provided values).
|
|
157
|
+
* - `'disabled'` — runtime supports prompts but suppresses them by default.
|
|
158
|
+
*
|
|
159
|
+
* `'unsupported'` is the only immutable state. For the others, the `--interactive`/`-i` flag
|
|
160
|
+
* and `cli()` preferences can override the default behavior.
|
|
161
|
+
*/
|
|
162
|
+
interactive?: InteractiveMode;
|
|
163
|
+
/**
|
|
164
|
+
* Prompt the user for input. Called during `cli()` for fields marked as interactive.
|
|
165
|
+
* When `interactive` is `true` and this is not provided, defaults to an Enquirer-based terminal prompt.
|
|
166
|
+
*/
|
|
167
|
+
prompt?: (config: InteractivePromptConfig) => Promise<unknown>;
|
|
168
|
+
/**
|
|
169
|
+
* Read a line of input from the user. Used by `repl()` for custom runtimes
|
|
170
|
+
* (web UIs, chat interfaces, testing).
|
|
171
|
+
* Returns the input string, `null` on EOF (e.g. Ctrl+D, closed connection),
|
|
172
|
+
* or `REPL_SIGINT` when the user presses Ctrl+C.
|
|
173
|
+
*
|
|
174
|
+
* When not provided, `repl()` uses a built-in Node.js readline session
|
|
175
|
+
* with command history (up/down arrows) and tab completion.
|
|
176
|
+
*/
|
|
177
|
+
readLine?: (prompt: string) => Promise<string | typeof REPL_SIGINT | null>;
|
|
178
|
+
/**
|
|
179
|
+
* Register a callback for process signals. Returns an unsubscribe function.
|
|
180
|
+
* The default runtime wires this to `process.on('SIGINT' | 'SIGTERM' | 'SIGHUP')`.
|
|
181
|
+
* Non-Node runtimes (web UIs, tests) can map their own cancellation semantics.
|
|
182
|
+
*
|
|
183
|
+
* When not provided, signal handling is disabled for this runtime.
|
|
184
|
+
*/
|
|
185
|
+
onSignal?: (callback: (signal: PadroneSignal) => void) => () => void;
|
|
186
|
+
/**
|
|
187
|
+
* Terminal/output capabilities. Used for ANSI detection, text wrapping, and TTY checks.
|
|
188
|
+
* The default runtime auto-detects from `process.stdout`. Non-terminal runtimes
|
|
189
|
+
* (web UIs, tests) should provide explicit values.
|
|
190
|
+
*/
|
|
191
|
+
terminal?: {
|
|
192
|
+
/** Number of columns in the terminal. Used for text wrapping. */columns?: number; /** Whether stdout is a TTY. Affects ANSI color output and interactive features. */
|
|
193
|
+
isTTY?: boolean;
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Force-exit the process. The default runtime wires this to `process.exit()`.
|
|
197
|
+
* Non-Node runtimes can throw an error or no-op.
|
|
198
|
+
*/
|
|
199
|
+
exit?: (code: number) => never;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Internal resolved runtime where all fields are guaranteed to be present.
|
|
203
|
+
* The `prompt`, `interactive`, and `readLine` fields remain optional since not all runtimes provide them.
|
|
204
|
+
*/
|
|
205
|
+
type ResolvedPadroneRuntime = Required<Omit<PadroneRuntime, 'prompt' | 'interactive' | 'readLine' | 'stdin' | 'theme' | 'onSignal' | 'terminal' | 'exit'>> & Pick<PadroneRuntime, 'prompt' | 'interactive' | 'readLine' | 'stdin' | 'theme' | 'onSignal' | 'terminal' | 'exit'>;
|
|
206
|
+
/**
|
|
207
|
+
* Sentinel value returned by the terminal REPL session when Ctrl+C is pressed.
|
|
208
|
+
* Distinguished from empty string (user pressed enter) and null (EOF/Ctrl+D).
|
|
209
|
+
*/
|
|
210
|
+
declare const REPL_SIGINT: unique symbol;
|
|
211
|
+
//#endregion
|
|
212
|
+
export { InteractiveMode, InteractivePromptConfig, PadroneBarAnimation, PadroneBarChar, PadroneBarConfig, PadroneProgress, PadroneProgressOptions, PadroneProgressShow, PadroneProgressUpdate, PadroneRuntime, PadroneSignal, PadroneSpinnerConfig, PadroneSpinnerPreset, REPL_SIGINT, ResolvedPadroneRuntime };
|
|
213
|
+
//# sourceMappingURL=runtime.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.mts","names":[],"sources":["../../src/core/runtime.ts"],"mappings":";;;;;KAIY,aAAA;AAAZ;AAAA,KAGY,qBAAA;EAA4C,OAAA;EAAkB,QAAA;EAAmB,aAAA;EAAyB,IAAA;AAAA;;;;;KAM1G,eAAA;EAN0G;;AAAI;AAM1H;;;;;;;;;;;EAeE,MAAA,GAAS,KAAA,EAAO,qBAAqB,WAI9B;EAFP,OAAA,GAAU,OAAA,kBAAyB,OAAA;IAAY,SAAA;EAAA,YAM7C;EAJF,IAAA,GAAO,OAAA,kBAAyB,OAAA;IAAY,SAAA;EAAA,YAa5C;EAXA,GAAA;IAaM,uFAXJ,KAAA,cAeQ;IAbR,IAAA,cAa2B;IAX3B,KAAA;EAAA,GAcQ;EAXV,IAAA;EAEA,KAAA,cAS8B;EAP9B,MAAA;AAAA;;KAIU,mBAAA;;KAGA,oBAAA;;;;;AAW0H;AAMtI;;;;KANY,oBAAA,GAAuB,oBAAA;EAAmC,MAAA;EAAmB,QAAA;EAAmB,IAAA,GAAO,mBAAmB;AAAA;AAcvG;AAK/B;;;KAbY,cAAA;;;;;;;KAQA,mBAAA;;;;KAKA,gBAAA;EAQE,8DANZ,KAAA,WAaO;EAXP,MAAA,GAAS,cAAA,EAWiB;EAT1B,KAAA,GAAQ,cAAA,EAYwB;EAVhC,SAAA,GAAY,mBAAA;EAaoB;;;;;;EANhC,IAAA,GAAO,mBAAA;AAAA;AAAA,KAGG,sBAAA;EACV,OAAA,GAAU,oBAAA,EAUI;EARd,GAAA,aAAgB,gBAAgB,EAkBP;EAhBzB,IAAA,YAgByB;EAdzB,GAAA,YAoBU;EAlBV,gBAAA;EAEA,cAAA;AAAA;;;;;;;;KAUU,eAAA;AA0BZ;;;;AAAA,KApBY,uBAAA;EAgCF,qCA9BR,IAAA,UA2Cc;EAzCd,OAAA,UAuDc;EArDd,IAAA,+DA0D8C;EAxD9C,OAAA;IAAY,KAAA;IAAe,KAAA;EAAA,KA2EiB;EAzE5C,OAAA;AAAA;;;;;;;;KAUU,cAAA;EAYF,uGAVR,MAAA,OAAa,IAAA,sBAmBb;EAjBA,KAAA,IAAS,IAAA,mBAqBP;EAnBF,IAAA,mBAqBE;EAnBF,GAAA,SAAY,MAAA,8BA+BZ;EA7BA,MAAA,GAAS,UAAA,WAkCT;EAhCA,KAAA,GAAQ,UAAA,GAAa,WAAA;EAgCX;;;;;;;;EAvBV,KAAA;IA0CY,oEAxCV,KAAA,YAiDA;IA/CA,IAAA,QAAY,OAAA,UAwDd;IAtDE,KAAA,QAAa,aAAA;EAAA;EAsDK;AAOtB;;;;;;;;;EAjDE,WAAA,GAAc,eAAA;EAiDqB;;;;EA5CnC,MAAA,IAAU,MAAA,EAAQ,uBAAA,KAA4B,OAAA;EA+C3B;AAAA;AAMrB;;;;AAAgD;;;EA3C9C,QAAA,IAAY,MAAA,aAAmB,OAAA,iBAAwB,WAAA;;;;;;;;EASvD,QAAA,IAAY,QAAA,GAAW,MAAA,EAAQ,aAAA;;;;;;EAO/B,QAAA;qEAEE,OAAA;IAEA,KAAA;EAAA;;;;;EAOF,IAAA,IAAQ,IAAA;AAAA;;;;;KAOE,sBAAA,GAAyB,QAAA,CACnC,IAAA,CAAK,cAAA,mGAEL,IAAA,CAAK,cAAA;;;;;cAMM,WAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/core/runtime.ts
|
|
2
|
+
/**
|
|
3
|
+
* Sentinel value returned by the terminal REPL session when Ctrl+C is pressed.
|
|
4
|
+
* Distinguished from empty string (user pressed enter) and null (EOF/Ctrl+D).
|
|
5
|
+
*/
|
|
6
|
+
const REPL_SIGINT = Symbol("REPL_SIGINT");
|
|
7
|
+
//#endregion
|
|
8
|
+
export { REPL_SIGINT };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=runtime.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../src/core/runtime.ts"],"sourcesContent":["import type { ColorConfig, ColorTheme } from '../output/colorizer.ts';\nimport type { HelpFormat } from '../output/formatter.ts';\n\n/** Process signals that Padrone can handle for graceful shutdown. */\nexport type PadroneSignal = 'SIGINT' | 'SIGTERM' | 'SIGHUP';\n\n/** Value accepted by `PadroneProgress.update()`. */\nexport type PadroneProgressUpdate = string | number | { message?: string; progress?: number; indeterminate?: boolean; time?: boolean };\n\n/**\n * A progress indicator instance (spinner, progress bar, etc).\n * Created by the runtime's `progress` factory and used to show loading state during command execution.\n */\nexport type PadroneProgress = {\n /**\n * Update the indicator.\n * - `string` — update the displayed message.\n * - `number` — set progress ratio (0–1). Values outside this range are clamped.\n * - `{ message?, progress?, indeterminate? }` — update message, progress, or both.\n *\n * Set `indeterminate: true` to force the bar into indeterminate mode (shows animation, no percentage).\n * This makes the bar visible even in `show: 'auto'` mode without providing a number.\n * Omitting `progress` (or passing a string) leaves the bar in its current state.\n * Setting `progress` when bar mode is not enabled is a no-op for the bar portion.\n *\n * Set `time: true` to start the elapsed timer on demand (useful when `time` was not set in options).\n * Set `time: false` to hide the elapsed timer.\n */\n update: (value: PadroneProgressUpdate) => void;\n /** Mark as succeeded and stop. Pass `null` to stop without rendering a final message. */\n succeed: (message?: string | null, options?: { indicator?: string }) => void;\n /** Mark as failed and stop. Pass `null` to stop without rendering a final message. */\n fail: (message?: string | null, options?: { indicator?: string }) => void;\n /** Control ETA (estimated time remaining) display at runtime. */\n eta: {\n /** Enable ETA tracking. Starts collecting samples from subsequent `update()` calls. */\n start: () => void;\n /** Disable ETA display. */\n stop: () => void;\n /** Clear collected samples and restart estimation. Useful between operation phases. */\n reset: () => void;\n };\n /** Stop without success/fail status. */\n stop: () => void;\n /** Temporarily hide the indicator so other output can be written cleanly. */\n pause: () => void;\n /** Redraw the indicator after a `pause()`. */\n resume: () => void;\n};\n\n/** Controls when a progress element (spinner or bar) is visible. */\nexport type PadroneProgressShow = 'auto' | 'always' | 'never';\n\n/** Built-in spinner presets. */\nexport type PadroneSpinnerPreset = 'dots' | 'line' | 'arc' | 'bounce';\n\n/**\n * Spinner configuration for progress indicators.\n * - A preset name (e.g., `'dots'`) to use built-in frames.\n * - `true` — default spinner with `show: 'always'` (visible even alongside a bar).\n * - An object with custom `frames`, `interval`, and/or `show`.\n * - `false` to disable the spinner (`show: 'never'`).\n *\n * Default `show` is `'auto'`: visible when the bar is not shown.\n */\nexport type PadroneSpinnerConfig = PadroneSpinnerPreset | boolean | { frames?: string[]; interval?: number; show?: PadroneProgressShow };\n\n/**\n * Options passed to the runtime's `progress` factory.\n */\n/** Common fill/empty character pairs for progress bars. */\nexport type PadroneBarChar = '█' | '░' | '▓' | '▒' | '─' | '━' | '■' | '□' | '#' | '-' | '=' | '·' | '▰' | '▱' | (string & {});\n\n/**\n * Built-in indeterminate bar animation presets.\n * - `'bounce'` — a filled segment slides back and forth (default).\n * - `'slide'` — a filled segment slides left-to-right and wraps around.\n * - `'pulse'` — the entire bar fades in and out using gradient characters (`░▒▓█▓▒░`).\n */\nexport type PadroneBarAnimation = 'bounce' | 'slide' | 'pulse';\n\n/**\n * Progress bar configuration.\n */\nexport type PadroneBarConfig = {\n /** Total width of the bar in characters. Defaults to `20`. */\n width?: number;\n /** Character used for the filled portion of the bar. Defaults to `'█'`. */\n filled?: PadroneBarChar;\n /** Character used for the empty portion of the bar. Defaults to `'░'`. */\n empty?: PadroneBarChar;\n /** Indeterminate animation style. Defaults to `'bounce'`. */\n animation?: PadroneBarAnimation;\n /**\n * When the bar is visible. Defaults to `'always'` when bar is enabled, `'auto'` when bar is not explicitly configured.\n * - `'always'` — bar is always shown (indeterminate until a number is provided).\n * - `'auto'` — bar is shown only after `update()` is called with a number.\n * - `'never'` — bar is never shown.\n */\n show?: PadroneProgressShow;\n};\n\nexport type PadroneProgressOptions = {\n spinner?: PadroneSpinnerConfig;\n /** Enable a progress bar. `true` for defaults (`show: 'always'`), or a `PadroneBarConfig` object. `false` to disable entirely. When omitted, bar defaults to `show: 'auto'` (appears when a number is provided). */\n bar?: boolean | PadroneBarConfig;\n /** Show elapsed time since the indicator started. Defaults to `false`. */\n time?: boolean;\n /** Show estimated time remaining based on progress rate. Requires numeric `update()` calls. Defaults to `false`. */\n eta?: boolean;\n /** Character/string shown before the success message. Defaults to `'✔'`. */\n successIndicator?: string;\n /** Character/string shown before the error message. Defaults to `'✖'`. */\n errorIndicator?: string;\n};\n\n/**\n * Controls interactive prompting capability and default behavior at the runtime level.\n * - `'supported'` — capable; caller decides.\n * - `'unsupported'` — hard veto; nothing can override.\n * - `'forced'` — capable and forces prompts by default.\n * - `'disabled'` — capable but suppresses prompts by default.\n */\nexport type InteractiveMode = 'supported' | 'unsupported' | 'forced' | 'disabled';\n\n/**\n * Configuration passed to the runtime's `prompt` function for interactive field prompting.\n * The prompt type and choices are auto-detected from the field's JSON schema.\n */\nexport type InteractivePromptConfig = {\n /** The field name being prompted. */\n name: string;\n /** Human-readable message/label for the prompt, derived from the field's description or name. */\n message: string;\n /** The prompt type, auto-detected from the JSON schema. */\n type: 'input' | 'confirm' | 'select' | 'multiselect' | 'password';\n /** Available choices for select/multiselect prompts. */\n choices?: { label: string; value: unknown }[];\n /** Default value from the schema. */\n default?: unknown;\n};\n\n/**\n * Defines the execution context for a Padrone program.\n * Abstracts all environment-dependent I/O so the CLI framework\n * can run outside of a terminal (e.g., web UIs, chat interfaces, testing).\n *\n * All fields are optional — unspecified fields fall back to the Node.js/Bun defaults.\n */\nexport type PadroneRuntime = {\n /** Write normal output (replaces console.log). Receives the raw value — runtime handles formatting. */\n output?: (...args: unknown[]) => void;\n /** Write error output (replaces console.error). */\n error?: (text: string) => void;\n /** Return the raw CLI arguments (replaces process.argv.slice(2)). */\n argv?: () => string[];\n /** Return environment variables (replaces process.env). */\n env?: () => Record<string, string | undefined>;\n /** Default help output format. */\n format?: HelpFormat | 'auto';\n /** Color theme for ANSI/console help output. A theme name or partial color config. */\n theme?: ColorTheme | ColorConfig;\n /**\n * Standard input abstraction. Provides methods to read piped data from stdin.\n * When not provided, defaults to reading from `process.stdin`.\n *\n * Used by commands that declare a `stdin` field in their arguments meta.\n * The framework reads stdin automatically during the validate phase and\n * injects the data into the specified argument field.\n */\n stdin?: {\n /** Whether stdin is a TTY (interactive terminal) vs a pipe/file. */\n isTTY?: boolean;\n /** Read all of stdin as a string. */\n text: () => Promise<string>;\n /** Async iterable of lines for streaming. */\n lines: () => AsyncIterable<string>;\n };\n /**\n * Controls interactive prompting capability and default behavior.\n * - `'supported'` — runtime can handle prompts; caller (flag/pref) decides whether to prompt. This is the default when `prompt` is provided.\n * - `'unsupported'` — runtime cannot handle prompts; hard veto that nothing can override.\n * - `'forced'` — runtime supports prompts and forces them by default (prompts even for provided values).\n * - `'disabled'` — runtime supports prompts but suppresses them by default.\n *\n * `'unsupported'` is the only immutable state. For the others, the `--interactive`/`-i` flag\n * and `cli()` preferences can override the default behavior.\n */\n interactive?: InteractiveMode;\n /**\n * Prompt the user for input. Called during `cli()` for fields marked as interactive.\n * When `interactive` is `true` and this is not provided, defaults to an Enquirer-based terminal prompt.\n */\n prompt?: (config: InteractivePromptConfig) => Promise<unknown>;\n /**\n * Read a line of input from the user. Used by `repl()` for custom runtimes\n * (web UIs, chat interfaces, testing).\n * Returns the input string, `null` on EOF (e.g. Ctrl+D, closed connection),\n * or `REPL_SIGINT` when the user presses Ctrl+C.\n *\n * When not provided, `repl()` uses a built-in Node.js readline session\n * with command history (up/down arrows) and tab completion.\n */\n readLine?: (prompt: string) => Promise<string | typeof REPL_SIGINT | null>;\n\n /**\n * Register a callback for process signals. Returns an unsubscribe function.\n * The default runtime wires this to `process.on('SIGINT' | 'SIGTERM' | 'SIGHUP')`.\n * Non-Node runtimes (web UIs, tests) can map their own cancellation semantics.\n *\n * When not provided, signal handling is disabled for this runtime.\n */\n onSignal?: (callback: (signal: PadroneSignal) => void) => () => void;\n\n /**\n * Terminal/output capabilities. Used for ANSI detection, text wrapping, and TTY checks.\n * The default runtime auto-detects from `process.stdout`. Non-terminal runtimes\n * (web UIs, tests) should provide explicit values.\n */\n terminal?: {\n /** Number of columns in the terminal. Used for text wrapping. */\n columns?: number;\n /** Whether stdout is a TTY. Affects ANSI color output and interactive features. */\n isTTY?: boolean;\n };\n\n /**\n * Force-exit the process. The default runtime wires this to `process.exit()`.\n * Non-Node runtimes can throw an error or no-op.\n */\n exit?: (code: number) => never;\n};\n\n/**\n * Internal resolved runtime where all fields are guaranteed to be present.\n * The `prompt`, `interactive`, and `readLine` fields remain optional since not all runtimes provide them.\n */\nexport type ResolvedPadroneRuntime = Required<\n Omit<PadroneRuntime, 'prompt' | 'interactive' | 'readLine' | 'stdin' | 'theme' | 'onSignal' | 'terminal' | 'exit'>\n> &\n Pick<PadroneRuntime, 'prompt' | 'interactive' | 'readLine' | 'stdin' | 'theme' | 'onSignal' | 'terminal' | 'exit'>;\n\n/**\n * Sentinel value returned by the terminal REPL session when Ctrl+C is pressed.\n * Distinguished from empty string (user pressed enter) and null (EOF/Ctrl+D).\n */\nexport const REPL_SIGINT = Symbol('REPL_SIGINT');\n\n/**\n * Internal session config for the REPL's persistent readline interface.\n */\nexport type ReplSessionConfig = {\n completer?: (line: string) => [string[], string];\n history?: string[];\n};\n"],"mappings":";;;;;AAsPA,MAAa,cAAc,OAAO,aAAa"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { coerceArgs, detectUnknownArgs, extractSchemaMetadata, getJsonSchema, parsePositionalConfig, preprocessArgs } from "./args.mjs";
|
|
2
|
+
import { getCommandRuntime } from "./commands.mjs";
|
|
3
|
+
import { thenMaybe } from "./results.mjs";
|
|
4
|
+
import { getNestedValue, parseCliInputToParts, setNestedValue } from "./parse.mjs";
|
|
5
|
+
//#region src/core/validate.ts
|
|
6
|
+
/**
|
|
7
|
+
* Parses CLI input to find the command and extract raw arguments without validation.
|
|
8
|
+
*/
|
|
9
|
+
function parseCommand(input, rootCommand, findCommandByName) {
|
|
10
|
+
input ??= getCommandRuntime(rootCommand).argv().join(" ") || void 0;
|
|
11
|
+
if (!input) {
|
|
12
|
+
const defaultCommand = findCommandByName("", rootCommand.commands);
|
|
13
|
+
if (defaultCommand) return {
|
|
14
|
+
command: defaultCommand,
|
|
15
|
+
rawArgs: {},
|
|
16
|
+
args: [],
|
|
17
|
+
unmatchedTerms: []
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
command: rootCommand,
|
|
21
|
+
rawArgs: {},
|
|
22
|
+
args: [],
|
|
23
|
+
unmatchedTerms: []
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const parts = parseCliInputToParts(input);
|
|
27
|
+
const terms = parts.filter((p) => p.type === "term").map((p) => p.value);
|
|
28
|
+
const argTokens = parts.filter((p) => p.type === "arg").map((p) => p.value);
|
|
29
|
+
let curCommand = rootCommand;
|
|
30
|
+
let unmatchedTerms = [];
|
|
31
|
+
if (terms[0] === rootCommand.name) terms.shift();
|
|
32
|
+
for (let i = 0; i < terms.length; i++) {
|
|
33
|
+
const found = findCommandByName(terms[i] || "", curCommand.commands);
|
|
34
|
+
if (found) curCommand = found;
|
|
35
|
+
else {
|
|
36
|
+
unmatchedTerms = terms.slice(i);
|
|
37
|
+
argTokens.unshift(...unmatchedTerms);
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (unmatchedTerms.length === 0 && curCommand.commands?.length) {
|
|
42
|
+
const defaultCommand = findCommandByName("", curCommand.commands);
|
|
43
|
+
if (defaultCommand) curCommand = defaultCommand;
|
|
44
|
+
}
|
|
45
|
+
if (!curCommand) return {
|
|
46
|
+
command: rootCommand,
|
|
47
|
+
rawArgs: {},
|
|
48
|
+
args: argTokens,
|
|
49
|
+
unmatchedTerms
|
|
50
|
+
};
|
|
51
|
+
const argsMeta = curCommand.meta?.fields;
|
|
52
|
+
const { flags, aliases, negatives, customNegation } = curCommand.argsSchema ? extractSchemaMetadata(curCommand.argsSchema, argsMeta, curCommand.meta?.autoAlias) : {
|
|
53
|
+
flags: {},
|
|
54
|
+
aliases: {},
|
|
55
|
+
negatives: {},
|
|
56
|
+
customNegation: /* @__PURE__ */ new Set()
|
|
57
|
+
};
|
|
58
|
+
const arrayArguments = /* @__PURE__ */ new Set();
|
|
59
|
+
if (curCommand.argsSchema) try {
|
|
60
|
+
const jsonSchema = getJsonSchema(curCommand.argsSchema);
|
|
61
|
+
if (jsonSchema.type === "object" && jsonSchema.properties) {
|
|
62
|
+
for (const [key, prop] of Object.entries(jsonSchema.properties)) if (prop?.type === "array") arrayArguments.add(key);
|
|
63
|
+
}
|
|
64
|
+
} catch {}
|
|
65
|
+
const argParts = parts.filter((p) => p.type === "named" || p.type === "alias");
|
|
66
|
+
const rawArgs = {};
|
|
67
|
+
for (const arg of argParts) {
|
|
68
|
+
let key;
|
|
69
|
+
if (arg.type === "alias" && arg.key.length === 1 && flags[arg.key[0]]) key = [flags[arg.key[0]]];
|
|
70
|
+
else if (arg.type === "named" && arg.key.length === 1 && aliases[arg.key[0]]) key = [aliases[arg.key[0]]];
|
|
71
|
+
else if (arg.type === "named" && arg.key.length === 1 && negatives[arg.key[0]]) {
|
|
72
|
+
setNestedValue(rawArgs, [negatives[arg.key[0]]], false);
|
|
73
|
+
continue;
|
|
74
|
+
} else key = arg.key;
|
|
75
|
+
const rootKey = key[0];
|
|
76
|
+
if (arg.type === "named" && arg.negated) {
|
|
77
|
+
if (customNegation.has(rootKey)) {
|
|
78
|
+
setNestedValue(rawArgs, [`no-${key.join(".")}`], false);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
setNestedValue(rawArgs, key, false);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const value = arg.value ?? true;
|
|
85
|
+
if (arrayArguments.has(rootKey)) {
|
|
86
|
+
const existing = getNestedValue(rawArgs, key);
|
|
87
|
+
if (existing !== void 0) if (Array.isArray(existing)) if (Array.isArray(value)) existing.push(...value);
|
|
88
|
+
else existing.push(value);
|
|
89
|
+
else if (Array.isArray(value)) setNestedValue(rawArgs, key, [existing, ...value]);
|
|
90
|
+
else setNestedValue(rawArgs, key, [existing, value]);
|
|
91
|
+
else setNestedValue(rawArgs, key, Array.isArray(value) ? value : [value]);
|
|
92
|
+
} else {
|
|
93
|
+
const existing = getNestedValue(rawArgs, key);
|
|
94
|
+
if (existing !== void 0) if (Array.isArray(existing)) if (Array.isArray(value)) existing.push(...value);
|
|
95
|
+
else existing.push(value);
|
|
96
|
+
else if (Array.isArray(value)) setNestedValue(rawArgs, key, [existing, ...value]);
|
|
97
|
+
else setNestedValue(rawArgs, key, [existing, value]);
|
|
98
|
+
else setNestedValue(rawArgs, key, value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
command: curCommand,
|
|
103
|
+
rawArgs,
|
|
104
|
+
args: argTokens,
|
|
105
|
+
unmatchedTerms
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Preprocesses raw arguments: maps positional arguments and performs auto-coercion.
|
|
110
|
+
* External data sources (stdin, env, config) are handled by extensions before this runs.
|
|
111
|
+
*/
|
|
112
|
+
function buildCommandArgs(command, rawArgs, positionalArgs) {
|
|
113
|
+
let preprocessedArgs = preprocessArgs(rawArgs, {
|
|
114
|
+
flags: {},
|
|
115
|
+
aliases: {}
|
|
116
|
+
});
|
|
117
|
+
let issues;
|
|
118
|
+
const positionalConfig = command.meta?.positional ? parsePositionalConfig(command.meta.positional) : [];
|
|
119
|
+
if (positionalConfig.length > 0) {
|
|
120
|
+
let argIndex = 0;
|
|
121
|
+
for (let i = 0; i < positionalConfig.length; i++) {
|
|
122
|
+
const { name, variadic } = positionalConfig[i];
|
|
123
|
+
if (argIndex >= positionalArgs.length) break;
|
|
124
|
+
if (name in preprocessedArgs) {
|
|
125
|
+
issues ??= [];
|
|
126
|
+
issues.push({
|
|
127
|
+
path: [name],
|
|
128
|
+
message: `Ambiguous argument "${name}": provided both positionally and as a named option`
|
|
129
|
+
});
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (variadic) {
|
|
133
|
+
const nonVariadicAfter = positionalConfig.slice(i + 1).filter((p) => !p.variadic).length;
|
|
134
|
+
const variadicEnd = positionalArgs.length - nonVariadicAfter;
|
|
135
|
+
preprocessedArgs[name] = positionalArgs.slice(argIndex, variadicEnd);
|
|
136
|
+
argIndex = variadicEnd;
|
|
137
|
+
} else if (i === positionalConfig.length - 1 && positionalArgs.length > argIndex + 1) {
|
|
138
|
+
preprocessedArgs[name] = positionalArgs.slice(argIndex).join(" ");
|
|
139
|
+
argIndex = positionalArgs.length;
|
|
140
|
+
} else {
|
|
141
|
+
preprocessedArgs[name] = positionalArgs[argIndex];
|
|
142
|
+
argIndex++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (command.argsSchema) preprocessedArgs = coerceArgs(preprocessedArgs, command.argsSchema);
|
|
147
|
+
return {
|
|
148
|
+
args: preprocessedArgs,
|
|
149
|
+
issues
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Detects unknown options in args that aren't defined in the schema.
|
|
154
|
+
* Returns unknown key info with suggestions, or empty array if schema is loose.
|
|
155
|
+
*/
|
|
156
|
+
function checkUnknownArgs(command, preprocessedArgs) {
|
|
157
|
+
if (!command.argsSchema) {
|
|
158
|
+
const unknowns = [];
|
|
159
|
+
for (const key of Object.keys(preprocessedArgs)) unknowns.push({ key });
|
|
160
|
+
return unknowns;
|
|
161
|
+
}
|
|
162
|
+
const argsMeta = command.meta?.fields;
|
|
163
|
+
const { flags, aliases, negatives } = extractSchemaMetadata(command.argsSchema, argsMeta, command.meta?.autoAlias);
|
|
164
|
+
return detectUnknownArgs(preprocessedArgs, command.argsSchema, flags, aliases, negatives);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Validates preprocessed arguments against the command's schema.
|
|
168
|
+
* First checks for unknown args (strict by default), then runs schema validation.
|
|
169
|
+
* Returns sync or async result depending on the schema's validate method.
|
|
170
|
+
*/
|
|
171
|
+
function validateCommandArgs(command, preprocessedArgs) {
|
|
172
|
+
const unknownArgs = checkUnknownArgs(command, preprocessedArgs);
|
|
173
|
+
if (unknownArgs.length > 0) return {
|
|
174
|
+
args: void 0,
|
|
175
|
+
argsResult: { issues: unknownArgs.map(({ key }) => ({
|
|
176
|
+
path: [key],
|
|
177
|
+
message: `Unknown option: "${key}"`
|
|
178
|
+
})) }
|
|
179
|
+
};
|
|
180
|
+
const argsParsed = command.argsSchema ? command.argsSchema["~standard"].validate(preprocessedArgs) : { value: {} };
|
|
181
|
+
const buildResult = (parsed) => ({
|
|
182
|
+
args: parsed.issues ? void 0 : parsed.value,
|
|
183
|
+
argsResult: parsed
|
|
184
|
+
});
|
|
185
|
+
return thenMaybe(argsParsed, buildResult);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Returns the list of known option names from a command's schema (for fuzzy suggestion).
|
|
189
|
+
*/
|
|
190
|
+
function getKnownOptionNames(command) {
|
|
191
|
+
if (!command.argsSchema) return [];
|
|
192
|
+
try {
|
|
193
|
+
const js = getJsonSchema(command.argsSchema);
|
|
194
|
+
if (js.type === "object" && js.properties) return Object.keys(js.properties);
|
|
195
|
+
} catch {}
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Formats validation issue messages for display.
|
|
200
|
+
*/
|
|
201
|
+
function formatIssueMessages(issues) {
|
|
202
|
+
return issues.map((i) => ` - ${i.path?.join(".") || "root"}: ${i.message}`).join("\n");
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Core validate function for parse() — preprocesses and validates CLI args.
|
|
206
|
+
* Used by the parse program method (lighter weight than the full exec pipeline).
|
|
207
|
+
* External data sources (stdin, env, config) are not resolved here — use eval() for that.
|
|
208
|
+
*/
|
|
209
|
+
function coreValidateForParse(command, rawArgs, positionalArgs) {
|
|
210
|
+
const { args: preprocessedArgs, issues } = buildCommandArgs(command, rawArgs, positionalArgs);
|
|
211
|
+
if (issues) return {
|
|
212
|
+
args: void 0,
|
|
213
|
+
argsResult: { issues }
|
|
214
|
+
};
|
|
215
|
+
return thenMaybe(validateCommandArgs(command, preprocessedArgs), (v) => v);
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
export { buildCommandArgs, checkUnknownArgs, coreValidateForParse, formatIssueMessages, getKnownOptionNames, parseCommand, validateCommandArgs };
|
|
219
|
+
|
|
220
|
+
//# sourceMappingURL=validate.mjs.map
|