padrone 1.5.0 → 1.6.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 +36 -0
- package/README.md +15 -11
- package/dist/{args-D5PNDyNu.mjs → args-Cnq0nwSM.mjs} +91 -41
- package/dist/args-Cnq0nwSM.mjs.map +1 -0
- package/dist/codegen/index.mjs +4 -4
- package/dist/codegen/index.mjs.map +1 -1
- package/dist/commands-B_gufyR9.mjs +514 -0
- package/dist/commands-B_gufyR9.mjs.map +1 -0
- package/dist/{completion.mjs → completion-BEuflbDO.mjs} +12 -82
- package/dist/completion-BEuflbDO.mjs.map +1 -0
- package/dist/docs/index.d.mts +4 -4
- package/dist/docs/index.d.mts.map +1 -1
- package/dist/docs/index.mjs +10 -12
- package/dist/docs/index.mjs.map +1 -1
- package/dist/{errors-BiVrBgi6.mjs → errors-CL63UOzt.mjs} +26 -3
- package/dist/errors-CL63UOzt.mjs.map +1 -0
- package/dist/{formatter-DtHzbP22.d.mts → formatter-DrvhDMrq.d.mts} +3 -3
- package/dist/formatter-DrvhDMrq.d.mts.map +1 -0
- package/dist/{help-bbmu9-qd.mjs → help-B5Kk83of.mjs} +151 -37
- package/dist/help-B5Kk83of.mjs.map +1 -0
- package/dist/{types-Ch8Mk6Qb.d.mts → index-BaU3X6dY.d.mts} +621 -750
- package/dist/index-BaU3X6dY.d.mts.map +1 -0
- package/dist/index.d.mts +735 -37
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3409 -1563
- package/dist/index.mjs.map +1 -1
- package/dist/{mcp-mLWIdUIu.mjs → mcp-BM-d0nZi.mjs} +13 -15
- package/dist/mcp-BM-d0nZi.mjs.map +1 -0
- package/dist/{serve-B0u43DK7.mjs → serve-Bk0JUlCj.mjs} +12 -14
- package/dist/serve-Bk0JUlCj.mjs.map +1 -0
- package/dist/{stream-BcC146Ud.mjs → stream-DC4H8YTx.mjs} +24 -3
- package/dist/stream-DC4H8YTx.mjs.map +1 -0
- package/dist/test.d.mts +5 -8
- package/dist/test.d.mts.map +1 -1
- package/dist/test.mjs +2 -13
- package/dist/test.mjs.map +1 -1
- package/dist/{update-check-CFX1FV3v.mjs → update-check-CZ2VqjnV.mjs} +16 -17
- package/dist/update-check-CZ2VqjnV.mjs.map +1 -0
- package/dist/zod.d.mts +2 -2
- package/dist/zod.d.mts.map +1 -1
- package/dist/zod.mjs +2 -2
- package/dist/zod.mjs.map +1 -1
- package/package.json +15 -12
- package/src/cli/completions.ts +14 -11
- package/src/cli/docs.ts +13 -10
- package/src/cli/doctor.ts +22 -18
- package/src/cli/index.ts +28 -82
- package/src/cli/init.ts +10 -7
- package/src/cli/link.ts +20 -16
- package/src/cli/wrap.ts +14 -11
- package/src/codegen/schema-to-code.ts +2 -2
- package/src/{args.ts → core/args.ts} +32 -225
- package/src/core/commands.ts +373 -0
- package/src/core/create.ts +268 -0
- package/src/core/default-runtime.ts +239 -0
- package/src/{errors.ts → core/errors.ts} +22 -0
- package/src/core/exec.ts +259 -0
- package/src/core/interceptors.ts +302 -0
- package/src/{parse.ts → core/parse.ts} +36 -89
- package/src/core/program-methods.ts +301 -0
- package/src/core/results.ts +229 -0
- package/src/core/runtime.ts +246 -0
- package/src/core/validate.ts +247 -0
- package/src/docs/index.ts +12 -13
- package/src/extension/auto-output.ts +95 -0
- package/src/extension/color.ts +38 -0
- package/src/extension/completion.ts +49 -0
- package/src/extension/config.ts +262 -0
- package/src/extension/env.ts +101 -0
- package/src/extension/help.ts +192 -0
- package/src/extension/index.ts +43 -0
- package/src/extension/ink.ts +93 -0
- package/src/extension/interactive.ts +106 -0
- package/src/extension/logger.ts +214 -0
- package/src/extension/man.ts +51 -0
- package/src/extension/mcp.ts +52 -0
- package/src/extension/progress-renderer.ts +338 -0
- package/src/extension/progress.ts +299 -0
- package/src/extension/repl.ts +94 -0
- package/src/extension/serve.ts +48 -0
- package/src/extension/signal.ts +87 -0
- package/src/extension/stdin.ts +62 -0
- package/src/extension/suggestions.ts +114 -0
- package/src/extension/timing.ts +81 -0
- package/src/extension/tracing.ts +175 -0
- package/src/extension/update-check.ts +77 -0
- package/src/extension/utils.ts +51 -0
- package/src/extension/version.ts +63 -0
- package/src/{completion.ts → feature/completion.ts} +12 -12
- package/src/{interactive.ts → feature/interactive.ts} +4 -4
- package/src/{mcp.ts → feature/mcp.ts} +12 -15
- package/src/{repl-loop.ts → feature/repl-loop.ts} +10 -13
- package/src/{serve.ts → feature/serve.ts} +11 -15
- package/src/feature/test.ts +262 -0
- package/src/{update-check.ts → feature/update-check.ts} +16 -16
- package/src/{wrap.ts → feature/wrap.ts} +10 -8
- package/src/index.ts +111 -30
- package/src/{formatter.ts → output/formatter.ts} +131 -31
- package/src/{help.ts → output/help.ts} +22 -8
- package/src/{zod.d.ts → schema/zod.d.ts} +1 -1
- package/src/schema/zod.ts +50 -0
- package/src/test.ts +2 -276
- package/src/types/args-meta.ts +151 -0
- package/src/types/builder.ts +697 -0
- package/src/types/command.ts +157 -0
- package/src/types/index.ts +59 -0
- package/src/types/interceptor.ts +296 -0
- package/src/types/preferences.ts +83 -0
- package/src/types/result.ts +71 -0
- package/src/types/schema.ts +19 -0
- package/src/util/dotenv.ts +244 -0
- package/src/{shell-utils.ts → util/shell-utils.ts} +26 -9
- package/src/{stream.ts → util/stream.ts} +27 -1
- package/src/{type-helpers.ts → util/type-helpers.ts} +23 -16
- package/src/{type-utils.ts → util/type-utils.ts} +71 -33
- package/src/util/utils.ts +51 -0
- package/src/zod.ts +1 -50
- package/dist/args-D5PNDyNu.mjs.map +0 -1
- package/dist/chunk-CjcI7cDX.mjs +0 -15
- package/dist/command-utils-B1D-HqCd.mjs +0 -1117
- package/dist/command-utils-B1D-HqCd.mjs.map +0 -1
- package/dist/completion.d.mts +0 -64
- package/dist/completion.d.mts.map +0 -1
- package/dist/completion.mjs.map +0 -1
- package/dist/errors-BiVrBgi6.mjs.map +0 -1
- package/dist/formatter-DtHzbP22.d.mts.map +0 -1
- package/dist/help-bbmu9-qd.mjs.map +0 -1
- package/dist/mcp-mLWIdUIu.mjs.map +0 -1
- package/dist/serve-B0u43DK7.mjs.map +0 -1
- package/dist/stream-BcC146Ud.mjs.map +0 -1
- package/dist/types-Ch8Mk6Qb.d.mts.map +0 -1
- package/dist/update-check-CFX1FV3v.mjs.map +0 -1
- package/src/command-utils.ts +0 -882
- package/src/create.ts +0 -1829
- package/src/runtime.ts +0 -497
- package/src/types.ts +0 -1291
- package/src/utils.ts +0 -140
- /package/src/{colorizer.ts → output/colorizer.ts} +0 -0
|
@@ -1,1117 +0,0 @@
|
|
|
1
|
-
import { n as __require, t as __exportAll } from "./chunk-CjcI7cDX.mjs";
|
|
2
|
-
import { a as extractSchemaMetadata, t as JSON_SCHEMA_OPTS } from "./args-D5PNDyNu.mjs";
|
|
3
|
-
//#region src/utils.ts
|
|
4
|
-
function getRootCommand(cmd) {
|
|
5
|
-
let current = cmd;
|
|
6
|
-
while (current.parent) current = current.parent;
|
|
7
|
-
return current;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Attempts to get the version from various sources:
|
|
11
|
-
* 1. Explicit version set on the command
|
|
12
|
-
* 2. npm_package_version environment variable (set by npm/yarn/pnpm when running scripts)
|
|
13
|
-
* 3. package.json in current or parent directories
|
|
14
|
-
* @param explicitVersion - Version explicitly set via .version()
|
|
15
|
-
* @returns The version string or '0.0.0' if not found
|
|
16
|
-
*/
|
|
17
|
-
function getVersion(explicitVersion) {
|
|
18
|
-
if (explicitVersion) return explicitVersion;
|
|
19
|
-
if (typeof process !== "undefined" && process.env?.npm_package_version) return process.env.npm_package_version;
|
|
20
|
-
if (typeof process !== "undefined") try {
|
|
21
|
-
const fs = __require("node:fs");
|
|
22
|
-
const path = __require("node:path");
|
|
23
|
-
let dir = process.cwd();
|
|
24
|
-
for (let i = 0; i < 10; i++) {
|
|
25
|
-
const pkgPath = path.join(dir, "package.json");
|
|
26
|
-
if (fs.existsSync(pkgPath)) {
|
|
27
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
28
|
-
if (pkg.version) return pkg.version;
|
|
29
|
-
}
|
|
30
|
-
const parentDir = path.dirname(dir);
|
|
31
|
-
if (parentDir === dir) break;
|
|
32
|
-
dir = parentDir;
|
|
33
|
-
}
|
|
34
|
-
} catch {}
|
|
35
|
-
return "0.0.0";
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Loads and parses a config file from the given path.
|
|
39
|
-
* Supports JSON, JSONC (JSON with comments), and attempts to parse other formats.
|
|
40
|
-
* @param configPath - Path to the config file
|
|
41
|
-
* @returns Parsed config data or undefined if loading fails
|
|
42
|
-
*/
|
|
43
|
-
function loadConfigFile(configPath) {
|
|
44
|
-
if (typeof process === "undefined") return void 0;
|
|
45
|
-
try {
|
|
46
|
-
const fs = __require("node:fs");
|
|
47
|
-
const path = __require("node:path");
|
|
48
|
-
const absolutePath = path.isAbsolute(configPath) ? configPath : path.resolve(process.cwd(), configPath);
|
|
49
|
-
if (!fs.existsSync(absolutePath)) {
|
|
50
|
-
console.error(`Config file not found: ${absolutePath}`);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const getContent = () => fs.readFileSync(absolutePath, "utf-8");
|
|
54
|
-
const ext = path.extname(absolutePath).toLowerCase();
|
|
55
|
-
if (ext === ".yaml" || ext === ".yml") return Bun.YAML.parse(getContent());
|
|
56
|
-
if (ext === ".toml") return Bun.TOML.parse(getContent());
|
|
57
|
-
if (ext === ".json") {
|
|
58
|
-
if (Bun.JSONC) return Bun.JSONC.parse(getContent());
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(getContent());
|
|
61
|
-
} catch {
|
|
62
|
-
return Bun.JSONC.parse(getContent());
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (ext === ".jsonc") return Bun.JSONC.parse(getContent());
|
|
66
|
-
if (ext === ".js" || ext === ".cjs" || ext === ".mjs" || ext === ".ts" || ext === ".cts" || ext === ".mts") return __require(absolutePath);
|
|
67
|
-
try {
|
|
68
|
-
return JSON.parse(getContent());
|
|
69
|
-
} catch {
|
|
70
|
-
console.error(`Unable to parse config file: ${absolutePath}`);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error(`Error loading config file: ${error}`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Searches for a config file from a list of possible file names.
|
|
80
|
-
* Searches in the current working directory.
|
|
81
|
-
* @param configFiles - Array of possible config file names to search for
|
|
82
|
-
* @returns The path to the first found config file, or undefined if none found
|
|
83
|
-
*/
|
|
84
|
-
function findConfigFile(configFiles) {
|
|
85
|
-
if (typeof process === "undefined" || !configFiles?.length) return void 0;
|
|
86
|
-
try {
|
|
87
|
-
const fs = __require("node:fs");
|
|
88
|
-
const path = __require("node:path");
|
|
89
|
-
const cwd = process.cwd();
|
|
90
|
-
for (const configFile of configFiles) {
|
|
91
|
-
const configPath = path.isAbsolute(configFile) ? configFile : path.resolve(cwd, configFile);
|
|
92
|
-
if (fs.existsSync(configPath)) return configPath;
|
|
93
|
-
}
|
|
94
|
-
} catch {}
|
|
95
|
-
}
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/runtime.ts
|
|
98
|
-
/**
|
|
99
|
-
* Default terminal prompt implementation powered by Enquirer.
|
|
100
|
-
* Lazily imported to avoid loading Enquirer when not needed.
|
|
101
|
-
*/
|
|
102
|
-
async function defaultTerminalPrompt(config) {
|
|
103
|
-
const Enquirer = (await import("enquirer")).default;
|
|
104
|
-
const question = {
|
|
105
|
-
type: config.type,
|
|
106
|
-
name: config.name,
|
|
107
|
-
message: config.message
|
|
108
|
-
};
|
|
109
|
-
if (config.default !== void 0) question.initial = config.default;
|
|
110
|
-
if (config.choices) question.choices = config.choices.map((c) => ({
|
|
111
|
-
name: String(c.value),
|
|
112
|
-
message: c.label
|
|
113
|
-
}));
|
|
114
|
-
return (await Enquirer.prompt(question))[config.name];
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Creates a persistent Node.js readline session for the REPL.
|
|
118
|
-
* Enables up/down arrow history navigation and tab completion.
|
|
119
|
-
* Used internally by `repl()` when no custom `readLine` is provided.
|
|
120
|
-
*/
|
|
121
|
-
/**
|
|
122
|
-
* Sentinel value returned by the terminal REPL session when Ctrl+C is pressed.
|
|
123
|
-
* Distinguished from empty string (user pressed enter) and null (EOF/Ctrl+D).
|
|
124
|
-
*/
|
|
125
|
-
const REPL_SIGINT = Symbol("REPL_SIGINT");
|
|
126
|
-
function createTerminalReplSession(config) {
|
|
127
|
-
let history = config.history ? [...config.history] : [];
|
|
128
|
-
let currentCompleter = config.completer;
|
|
129
|
-
return {
|
|
130
|
-
set completer(fn) {
|
|
131
|
-
currentCompleter = fn;
|
|
132
|
-
},
|
|
133
|
-
async question(prompt) {
|
|
134
|
-
const { createInterface } = await import("node:readline");
|
|
135
|
-
const opts = {
|
|
136
|
-
input: process.stdin,
|
|
137
|
-
output: process.stdout,
|
|
138
|
-
terminal: true,
|
|
139
|
-
history: [...history],
|
|
140
|
-
historySize: Math.max(history.length, 1e3)
|
|
141
|
-
};
|
|
142
|
-
if (currentCompleter) opts.completer = currentCompleter;
|
|
143
|
-
const rl = createInterface(opts);
|
|
144
|
-
return new Promise((resolve) => {
|
|
145
|
-
let resolved = false;
|
|
146
|
-
const settle = (value) => {
|
|
147
|
-
if (resolved) return;
|
|
148
|
-
resolved = true;
|
|
149
|
-
rl.close();
|
|
150
|
-
resolve(value);
|
|
151
|
-
};
|
|
152
|
-
rl.question(prompt, (answer) => {
|
|
153
|
-
if (Array.isArray(rl.history)) history = [...rl.history];
|
|
154
|
-
settle(answer);
|
|
155
|
-
});
|
|
156
|
-
rl.once("SIGINT", () => {
|
|
157
|
-
process.stdout.write("\n");
|
|
158
|
-
settle(REPL_SIGINT);
|
|
159
|
-
});
|
|
160
|
-
rl.once("close", () => {
|
|
161
|
-
process.stdout.write("\n");
|
|
162
|
-
settle(null);
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
},
|
|
166
|
-
close() {}
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Auto-detect interactive mode when not explicitly set.
|
|
171
|
-
* Returns 'disabled' in CI environments or non-TTY contexts, 'supported' otherwise.
|
|
172
|
-
*/
|
|
173
|
-
function detectInteractiveMode() {
|
|
174
|
-
if (typeof process === "undefined") return "disabled";
|
|
175
|
-
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return "disabled";
|
|
176
|
-
if (!process.stdout?.isTTY) return "disabled";
|
|
177
|
-
return "supported";
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Creates the default Node.js/Bun runtime.
|
|
181
|
-
*/
|
|
182
|
-
/**
|
|
183
|
-
* Creates a default stdin reader from `process.stdin`.
|
|
184
|
-
* Only created when a command actually declares a `stdin` meta field.
|
|
185
|
-
*/
|
|
186
|
-
function createDefaultStdin() {
|
|
187
|
-
return {
|
|
188
|
-
get isTTY() {
|
|
189
|
-
if (typeof process === "undefined") return true;
|
|
190
|
-
return process.stdin?.isTTY === true;
|
|
191
|
-
},
|
|
192
|
-
async text() {
|
|
193
|
-
if (typeof process === "undefined") return "";
|
|
194
|
-
const chunks = [];
|
|
195
|
-
for await (const chunk of process.stdin) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
196
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
197
|
-
},
|
|
198
|
-
async *lines() {
|
|
199
|
-
if (typeof process === "undefined") return;
|
|
200
|
-
const { createInterface } = await import("node:readline");
|
|
201
|
-
const rl = createInterface({ input: process.stdin });
|
|
202
|
-
try {
|
|
203
|
-
for await (const line of rl) yield line;
|
|
204
|
-
} finally {
|
|
205
|
-
rl.close();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
const spinnerPresets = {
|
|
211
|
-
dots: [
|
|
212
|
-
"⠋",
|
|
213
|
-
"⠙",
|
|
214
|
-
"⠹",
|
|
215
|
-
"⠸",
|
|
216
|
-
"⠼",
|
|
217
|
-
"⠴",
|
|
218
|
-
"⠦",
|
|
219
|
-
"⠧",
|
|
220
|
-
"⠇",
|
|
221
|
-
"⠏"
|
|
222
|
-
],
|
|
223
|
-
line: [
|
|
224
|
-
"-",
|
|
225
|
-
"\\",
|
|
226
|
-
"|",
|
|
227
|
-
"/"
|
|
228
|
-
],
|
|
229
|
-
arc: [
|
|
230
|
-
"◜",
|
|
231
|
-
"◠",
|
|
232
|
-
"◝",
|
|
233
|
-
"◞",
|
|
234
|
-
"◡",
|
|
235
|
-
"◟"
|
|
236
|
-
],
|
|
237
|
-
bounce: [
|
|
238
|
-
"⠁",
|
|
239
|
-
"⠂",
|
|
240
|
-
"⠄",
|
|
241
|
-
"⡀",
|
|
242
|
-
"⢀",
|
|
243
|
-
"⠠",
|
|
244
|
-
"⠐",
|
|
245
|
-
"⠈"
|
|
246
|
-
]
|
|
247
|
-
};
|
|
248
|
-
function resolveSpinnerConfig(config) {
|
|
249
|
-
if (config === false) return {
|
|
250
|
-
frames: [],
|
|
251
|
-
interval: 80,
|
|
252
|
-
disabled: true
|
|
253
|
-
};
|
|
254
|
-
if (typeof config === "string") return {
|
|
255
|
-
frames: spinnerPresets[config],
|
|
256
|
-
interval: 80,
|
|
257
|
-
disabled: false
|
|
258
|
-
};
|
|
259
|
-
if (typeof config === "object") return {
|
|
260
|
-
frames: config.frames ?? spinnerPresets.dots,
|
|
261
|
-
interval: config.interval ?? 80,
|
|
262
|
-
disabled: false
|
|
263
|
-
};
|
|
264
|
-
return {
|
|
265
|
-
frames: spinnerPresets.dots,
|
|
266
|
-
interval: 80,
|
|
267
|
-
disabled: false
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Creates a built-in terminal spinner. Returns a no-op indicator in non-TTY/CI environments.
|
|
272
|
-
*/
|
|
273
|
-
function createTerminalSpinner(message, options) {
|
|
274
|
-
const { frames, interval, disabled: spinnerDisabled } = resolveSpinnerConfig(options?.spinner);
|
|
275
|
-
const successIcon = options?.successIndicator ?? "✔";
|
|
276
|
-
const errorIcon = options?.errorIndicator ?? "✖";
|
|
277
|
-
const formatFinal = (icon, msg) => icon ? `${icon} ${msg}\n` : `${msg}\n`;
|
|
278
|
-
if (typeof process === "undefined" || !process.stderr?.isTTY) return {
|
|
279
|
-
update() {},
|
|
280
|
-
succeed(msg, opts) {
|
|
281
|
-
if (msg === null) return;
|
|
282
|
-
const icon = opts?.indicator ?? successIcon;
|
|
283
|
-
if (msg || message) process?.stderr?.write?.(formatFinal(icon, msg || message));
|
|
284
|
-
},
|
|
285
|
-
fail(msg, opts) {
|
|
286
|
-
if (msg === null) return;
|
|
287
|
-
const icon = opts?.indicator ?? errorIcon;
|
|
288
|
-
if (msg || message) process?.stderr?.write?.(formatFinal(icon, msg || message));
|
|
289
|
-
},
|
|
290
|
-
stop() {},
|
|
291
|
-
pause() {},
|
|
292
|
-
resume() {}
|
|
293
|
-
};
|
|
294
|
-
if (spinnerDisabled && !message) return {
|
|
295
|
-
update() {},
|
|
296
|
-
succeed() {},
|
|
297
|
-
fail() {},
|
|
298
|
-
stop() {},
|
|
299
|
-
pause() {},
|
|
300
|
-
resume() {}
|
|
301
|
-
};
|
|
302
|
-
let frame = 0;
|
|
303
|
-
let text = message;
|
|
304
|
-
let stopped = false;
|
|
305
|
-
let paused = false;
|
|
306
|
-
const writeStderr = process.stderr.write.bind(process.stderr);
|
|
307
|
-
const writeStdout = process.stdout.write.bind(process.stdout);
|
|
308
|
-
const clearLine = () => writeStderr("\x1B[2K\r");
|
|
309
|
-
const render = () => {
|
|
310
|
-
if (paused || stopped) return;
|
|
311
|
-
if (spinnerDisabled) {
|
|
312
|
-
if (text) writeStderr(`\x1b[2K\r${text}`);
|
|
313
|
-
} else {
|
|
314
|
-
const prefix = frames[frame] ?? "";
|
|
315
|
-
writeStderr(`\x1b[2K\r${text ? `${prefix} ${text}` : prefix}`);
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
const timer = spinnerDisabled ? void 0 : setInterval(() => {
|
|
319
|
-
frame = (frame + 1) % frames.length;
|
|
320
|
-
render();
|
|
321
|
-
}, interval);
|
|
322
|
-
render();
|
|
323
|
-
const clear = () => {
|
|
324
|
-
if (stopped) return;
|
|
325
|
-
stopped = true;
|
|
326
|
-
paused = false;
|
|
327
|
-
if (timer) clearInterval(timer);
|
|
328
|
-
clearLine();
|
|
329
|
-
};
|
|
330
|
-
return {
|
|
331
|
-
update(msg) {
|
|
332
|
-
if (stopped) return;
|
|
333
|
-
text = msg;
|
|
334
|
-
render();
|
|
335
|
-
},
|
|
336
|
-
succeed(msg, opts) {
|
|
337
|
-
clear();
|
|
338
|
-
if (msg === null) return;
|
|
339
|
-
const finalMsg = msg ?? text;
|
|
340
|
-
const icon = opts?.indicator ?? successIcon;
|
|
341
|
-
if (finalMsg) writeStderr(formatFinal(icon, finalMsg));
|
|
342
|
-
},
|
|
343
|
-
fail(msg, opts) {
|
|
344
|
-
clear();
|
|
345
|
-
if (msg === null) return;
|
|
346
|
-
const finalMsg = msg ?? text;
|
|
347
|
-
const icon = opts?.indicator ?? errorIcon;
|
|
348
|
-
if (finalMsg) writeStderr(formatFinal(icon, finalMsg));
|
|
349
|
-
},
|
|
350
|
-
stop() {
|
|
351
|
-
clear();
|
|
352
|
-
},
|
|
353
|
-
pause() {
|
|
354
|
-
if (stopped || paused) return;
|
|
355
|
-
paused = true;
|
|
356
|
-
clearLine();
|
|
357
|
-
writeStdout("\x1B[2K\r");
|
|
358
|
-
},
|
|
359
|
-
resume() {
|
|
360
|
-
if (stopped || !paused) return;
|
|
361
|
-
paused = false;
|
|
362
|
-
render();
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
function createDefaultRuntime() {
|
|
367
|
-
return {
|
|
368
|
-
output: (...args) => console.log(...args),
|
|
369
|
-
error: (text) => console.error(text),
|
|
370
|
-
argv: () => typeof process !== "undefined" ? process.argv.slice(2) : [],
|
|
371
|
-
env: () => typeof process !== "undefined" ? process.env : {},
|
|
372
|
-
format: "auto",
|
|
373
|
-
loadConfigFile,
|
|
374
|
-
findFile: findConfigFile,
|
|
375
|
-
prompt: defaultTerminalPrompt,
|
|
376
|
-
interactive: detectInteractiveMode(),
|
|
377
|
-
progress: createTerminalSpinner
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Merges a partial runtime with the default runtime.
|
|
382
|
-
*/
|
|
383
|
-
/**
|
|
384
|
-
* Returns the stdin abstraction: custom runtime stdin > default process.stdin.
|
|
385
|
-
* Returns `undefined` when no custom stdin is provided and process.stdin is not piped.
|
|
386
|
-
*/
|
|
387
|
-
function resolveStdin(partial) {
|
|
388
|
-
if (partial?.stdin) return partial.stdin;
|
|
389
|
-
const defaultStdin = createDefaultStdin();
|
|
390
|
-
if (defaultStdin.isTTY) return void 0;
|
|
391
|
-
return defaultStdin;
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Like `resolveStdin`, but always returns a stdin source even when it's a TTY.
|
|
395
|
-
* Used for async streams which support interactive (non-piped) input.
|
|
396
|
-
*/
|
|
397
|
-
function resolveStdinAlways(partial) {
|
|
398
|
-
if (partial?.stdin) return partial.stdin;
|
|
399
|
-
return createDefaultStdin();
|
|
400
|
-
}
|
|
401
|
-
function resolveRuntime(partial) {
|
|
402
|
-
const defaults = createDefaultRuntime();
|
|
403
|
-
if (!partial) return defaults;
|
|
404
|
-
return {
|
|
405
|
-
output: partial.output ?? defaults.output,
|
|
406
|
-
error: partial.error ?? defaults.error,
|
|
407
|
-
argv: partial.argv ?? defaults.argv,
|
|
408
|
-
env: partial.env ?? defaults.env,
|
|
409
|
-
format: partial.format ?? defaults.format,
|
|
410
|
-
loadConfigFile: partial.loadConfigFile ?? defaults.loadConfigFile,
|
|
411
|
-
findFile: partial.findFile ?? defaults.findFile,
|
|
412
|
-
interactive: partial.interactive ?? defaults.interactive,
|
|
413
|
-
prompt: partial.prompt ?? defaults.prompt,
|
|
414
|
-
readLine: partial.readLine ?? defaults.readLine,
|
|
415
|
-
progress: partial.progress ?? defaults.progress,
|
|
416
|
-
stdin: partial.stdin,
|
|
417
|
-
theme: partial.theme
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
//#endregion
|
|
421
|
-
//#region src/command-utils.ts
|
|
422
|
-
var command_utils_exports = /* @__PURE__ */ __exportAll({
|
|
423
|
-
asyncSchema: () => asyncSchema,
|
|
424
|
-
buildInputSchema: () => buildInputSchema,
|
|
425
|
-
buildReplCompleter: () => buildReplCompleter,
|
|
426
|
-
collectEndpoints: () => collectEndpoints,
|
|
427
|
-
commandSymbol: () => commandSymbol,
|
|
428
|
-
configKeys: () => configKeys,
|
|
429
|
-
createLazyIndicator: () => createLazyIndicator,
|
|
430
|
-
createProgress: () => createProgress,
|
|
431
|
-
drainValue: () => drainValue,
|
|
432
|
-
errorResult: () => errorResult,
|
|
433
|
-
findCommandByName: () => findCommandByName,
|
|
434
|
-
getCommand: () => getCommand,
|
|
435
|
-
getCommandRuntime: () => getCommandRuntime,
|
|
436
|
-
hasInteractiveConfig: () => hasInteractiveConfig,
|
|
437
|
-
isAsyncBranded: () => isAsyncBranded,
|
|
438
|
-
isAsyncIterator: () => isAsyncIterator,
|
|
439
|
-
isIterator: () => isIterator,
|
|
440
|
-
lazyResolver: () => lazyResolver,
|
|
441
|
-
makeThenable: () => makeThenable,
|
|
442
|
-
mergeCommands: () => mergeCommands,
|
|
443
|
-
noop: () => noop,
|
|
444
|
-
noopIndicator: () => noopIndicator,
|
|
445
|
-
outputValue: () => outputValue,
|
|
446
|
-
repathCommandTree: () => repathCommandTree,
|
|
447
|
-
resolveAllCommands: () => resolveAllCommands,
|
|
448
|
-
resolveCommand: () => resolveCommand,
|
|
449
|
-
resolveProgressMessage: () => resolveProgressMessage,
|
|
450
|
-
runPluginChain: () => runPluginChain,
|
|
451
|
-
serializeArgsToFlags: () => serializeArgsToFlags,
|
|
452
|
-
suggestSimilar: () => suggestSimilar,
|
|
453
|
-
thenMaybe: () => thenMaybe,
|
|
454
|
-
warnIfUnexpectedAsync: () => warnIfUnexpectedAsync,
|
|
455
|
-
withDrain: () => withDrain,
|
|
456
|
-
withPromiseDrain: () => withPromiseDrain,
|
|
457
|
-
wrapWithLifecycle: () => wrapWithLifecycle
|
|
458
|
-
});
|
|
459
|
-
const lazyResolver = Symbol("lazyResolver");
|
|
460
|
-
/** Resolves a lazy command in place by calling its stored resolver. No-op if already resolved. */
|
|
461
|
-
function resolveCommand(cmd) {
|
|
462
|
-
const resolver = cmd[lazyResolver];
|
|
463
|
-
if (resolver) {
|
|
464
|
-
delete cmd[lazyResolver];
|
|
465
|
-
resolver(cmd);
|
|
466
|
-
}
|
|
467
|
-
return cmd;
|
|
468
|
-
}
|
|
469
|
-
/** Recursively resolves a command and all its descendants. */
|
|
470
|
-
function resolveAllCommands(cmd) {
|
|
471
|
-
resolveCommand(cmd);
|
|
472
|
-
if (cmd.commands) for (const sub of cmd.commands) resolveAllCommands(sub);
|
|
473
|
-
}
|
|
474
|
-
/** Extracts the underlying command from a program/builder and resolves the full command tree. */
|
|
475
|
-
function getCommand(program) {
|
|
476
|
-
const cmd = commandSymbol in program ? program[commandSymbol] : program;
|
|
477
|
-
resolveAllCommands(cmd);
|
|
478
|
-
return cmd;
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Brands a schema as async, signaling that its `validate()` may return a Promise.
|
|
482
|
-
* When an async-branded schema is passed to `.arguments()`, `.configFile()`, or `.env()`,
|
|
483
|
-
* the command's `parse()` and `cli()` will return Promises.
|
|
484
|
-
*
|
|
485
|
-
* @example
|
|
486
|
-
* ```ts
|
|
487
|
-
* const schema = asyncSchema(z.object({
|
|
488
|
-
* name: z.string(),
|
|
489
|
-
* }).check(async (data) => {
|
|
490
|
-
* // async validation logic
|
|
491
|
-
* }));
|
|
492
|
-
*
|
|
493
|
-
* const program = createPadrone('app')
|
|
494
|
-
* .command('greet', (c) => c.arguments(schema).action((args) => args.name));
|
|
495
|
-
*
|
|
496
|
-
* // parse() now returns Promise<PadroneParseResult>
|
|
497
|
-
* const result = await program.parse('greet --name world');
|
|
498
|
-
* ```
|
|
499
|
-
*/
|
|
500
|
-
function asyncSchema(schema) {
|
|
501
|
-
return Object.assign(schema, { "~async": true });
|
|
502
|
-
}
|
|
503
|
-
const commandSymbol = Symbol("padrone_command");
|
|
504
|
-
const noop = () => void 0;
|
|
505
|
-
/** Config keys that are merged when overriding a command. */
|
|
506
|
-
const configKeys = [
|
|
507
|
-
"title",
|
|
508
|
-
"description",
|
|
509
|
-
"version",
|
|
510
|
-
"deprecated",
|
|
511
|
-
"hidden",
|
|
512
|
-
"mutation",
|
|
513
|
-
"needsApproval",
|
|
514
|
-
"autoOutput",
|
|
515
|
-
"updateCheck"
|
|
516
|
-
];
|
|
517
|
-
/**
|
|
518
|
-
* Merges an existing command with an override.
|
|
519
|
-
* - Config fields are shallow-merged (new overrides old).
|
|
520
|
-
* - Action, arguments, meta, config schema, env schema are taken from the override if set.
|
|
521
|
-
* - Subcommands are recursively merged by name.
|
|
522
|
-
*/
|
|
523
|
-
function mergeCommands(existing, override) {
|
|
524
|
-
resolveCommand(existing);
|
|
525
|
-
resolveCommand(override);
|
|
526
|
-
const merged = { ...existing };
|
|
527
|
-
for (const key of configKeys) if (override[key] !== void 0) merged[key] = override[key];
|
|
528
|
-
if (override.action !== existing.action) merged.action = override.action;
|
|
529
|
-
if (override.argsSchema !== existing.argsSchema) merged.argsSchema = override.argsSchema;
|
|
530
|
-
if (override.meta !== existing.meta) merged.meta = override.meta;
|
|
531
|
-
if (override.configSchema !== existing.configSchema) merged.configSchema = override.configSchema;
|
|
532
|
-
if (override.envSchema !== existing.envSchema) merged.envSchema = override.envSchema;
|
|
533
|
-
if (override.configFiles !== existing.configFiles) merged.configFiles = override.configFiles;
|
|
534
|
-
if (override.isAsync !== existing.isAsync) merged.isAsync = override.isAsync || existing.isAsync;
|
|
535
|
-
if (override.runtime !== existing.runtime) merged.runtime = override.runtime;
|
|
536
|
-
if (override.plugins !== existing.plugins) merged.plugins = override.plugins;
|
|
537
|
-
if (override.aliases !== existing.aliases) merged.aliases = override.aliases;
|
|
538
|
-
if (override.progress !== existing.progress) merged.progress = override.progress;
|
|
539
|
-
if (override.commands) {
|
|
540
|
-
const baseCommands = [...existing.commands || []];
|
|
541
|
-
for (const overrideChild of override.commands) {
|
|
542
|
-
const existingIndex = baseCommands.findIndex((c) => c.name === overrideChild.name);
|
|
543
|
-
if (existingIndex >= 0) baseCommands[existingIndex] = mergeCommands(baseCommands[existingIndex], overrideChild);
|
|
544
|
-
else baseCommands.push(overrideChild);
|
|
545
|
-
}
|
|
546
|
-
merged.commands = baseCommands;
|
|
547
|
-
}
|
|
548
|
-
return merged;
|
|
549
|
-
}
|
|
550
|
-
/**
|
|
551
|
-
* Maps over a value that may or may not be a Promise.
|
|
552
|
-
* If the value is a Promise, chains with `.then()`. Otherwise, calls the function synchronously.
|
|
553
|
-
* This preserves sync behavior for sync schemas and async behavior for async schemas.
|
|
554
|
-
*/
|
|
555
|
-
function thenMaybe(value, fn) {
|
|
556
|
-
if (value instanceof Promise) return value.then(fn);
|
|
557
|
-
return fn(value);
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Makes a sync result object thenable by adding `.then()`, `.catch()`, and `.finally()` methods.
|
|
561
|
-
* If the value is already a Promise, returns it as-is.
|
|
562
|
-
* This allows users to write `await program.cli()` or `program.cli().then(...)` regardless of sync/async.
|
|
563
|
-
*
|
|
564
|
-
* The `.then()` resolves with a plain copy (without thenable methods) to avoid infinite
|
|
565
|
-
* recursive unwrapping by the Promise resolution algorithm.
|
|
566
|
-
*/
|
|
567
|
-
function makeThenable(value) {
|
|
568
|
-
if (value instanceof Promise) return value;
|
|
569
|
-
if (value !== null && typeof value === "object" && !("then" in value)) {
|
|
570
|
-
const toPlain = () => {
|
|
571
|
-
const plain = { ...value };
|
|
572
|
-
delete plain.then;
|
|
573
|
-
delete plain.catch;
|
|
574
|
-
delete plain.finally;
|
|
575
|
-
return plain;
|
|
576
|
-
};
|
|
577
|
-
value.then = (onfulfilled, onrejected) => {
|
|
578
|
-
try {
|
|
579
|
-
const result = onfulfilled ? onfulfilled(toPlain()) : toPlain();
|
|
580
|
-
return Promise.resolve(result);
|
|
581
|
-
} catch (err) {
|
|
582
|
-
if (onrejected) return Promise.resolve(onrejected(err));
|
|
583
|
-
return Promise.reject(err);
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
value.catch = (onrejected) => value.then(void 0, onrejected);
|
|
587
|
-
value.finally = (onfinally) => value.then((v) => {
|
|
588
|
-
onfinally?.();
|
|
589
|
-
return v;
|
|
590
|
-
}, (err) => {
|
|
591
|
-
onfinally?.();
|
|
592
|
-
throw err;
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
return value;
|
|
596
|
-
}
|
|
597
|
-
/**
|
|
598
|
-
* Wraps a Promise to include a `drain()` method at the top level.
|
|
599
|
-
* This allows `await promise.drain()` without first awaiting the promise.
|
|
600
|
-
* Since cli/eval never reject, this just delegates to the resolved result's `drain()`.
|
|
601
|
-
*/
|
|
602
|
-
function withPromiseDrain(promise) {
|
|
603
|
-
promise.drain = async () => {
|
|
604
|
-
return (await promise).drain();
|
|
605
|
-
};
|
|
606
|
-
return promise;
|
|
607
|
-
}
|
|
608
|
-
function isIterator(value) {
|
|
609
|
-
return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
|
|
610
|
-
}
|
|
611
|
-
function isAsyncIterator(value) {
|
|
612
|
-
return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Writes a command's return value to output, handling promises, iterators, and async iterators.
|
|
616
|
-
* Values are passed directly to the output function without stringification —
|
|
617
|
-
* runtimes like Node/Bun already format objects via console.log.
|
|
618
|
-
* Returns void or a Promise depending on whether async consumption is needed.
|
|
619
|
-
*/
|
|
620
|
-
function outputValue(value, output) {
|
|
621
|
-
if (value == null) return;
|
|
622
|
-
if (isAsyncIterator(value)) return (async () => {
|
|
623
|
-
const iter = value[Symbol.asyncIterator]();
|
|
624
|
-
while (true) {
|
|
625
|
-
const { done, value: item } = await iter.next();
|
|
626
|
-
if (done) break;
|
|
627
|
-
if (item != null) output(item);
|
|
628
|
-
}
|
|
629
|
-
})();
|
|
630
|
-
if (typeof value !== "string" && !Array.isArray(value) && isIterator(value)) {
|
|
631
|
-
const iter = value[Symbol.iterator]();
|
|
632
|
-
while (true) {
|
|
633
|
-
const { done, value: item } = iter.next();
|
|
634
|
-
if (done) break;
|
|
635
|
-
if (item != null) output(item);
|
|
636
|
-
}
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
if (value instanceof Promise) return value.then((resolved) => outputValue(resolved, output));
|
|
640
|
-
output(value);
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* Resolves a result value by unwrapping Promises and collecting iterables into arrays.
|
|
644
|
-
* This is the runtime counterpart of the `Drained<T>` type.
|
|
645
|
-
*/
|
|
646
|
-
async function drainValue(value) {
|
|
647
|
-
if (value instanceof Promise) return drainValue(await value);
|
|
648
|
-
if (isAsyncIterator(value)) {
|
|
649
|
-
const items = [];
|
|
650
|
-
const iter = value[Symbol.asyncIterator]();
|
|
651
|
-
while (true) {
|
|
652
|
-
const { done, value: item } = await iter.next();
|
|
653
|
-
if (done) break;
|
|
654
|
-
items.push(item);
|
|
655
|
-
}
|
|
656
|
-
return items;
|
|
657
|
-
}
|
|
658
|
-
if (typeof value !== "string" && !Array.isArray(value) && isIterator(value)) {
|
|
659
|
-
const items = [];
|
|
660
|
-
const iter = value[Symbol.iterator]();
|
|
661
|
-
while (true) {
|
|
662
|
-
const { done, value: item } = iter.next();
|
|
663
|
-
if (done) break;
|
|
664
|
-
items.push(item);
|
|
665
|
-
}
|
|
666
|
-
return items;
|
|
667
|
-
}
|
|
668
|
-
return value;
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Attaches a `drain()` method to a command result object.
|
|
672
|
-
* If the result has an `error` field, `drain()` returns `{ error }`.
|
|
673
|
-
* Otherwise, resolves the result (unwrapping Promises, collecting iterables), catches errors,
|
|
674
|
-
* and returns a discriminated union `{ value } | { error }` that never throws.
|
|
675
|
-
*/
|
|
676
|
-
function withDrain(obj) {
|
|
677
|
-
obj.drain = async () => {
|
|
678
|
-
if ("error" in obj && obj.error !== void 0) return { error: obj.error };
|
|
679
|
-
try {
|
|
680
|
-
return { value: await drainValue(obj.result) };
|
|
681
|
-
} catch (err) {
|
|
682
|
-
return { error: err };
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
return obj;
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Creates an error command result with a `drain()` that returns the error.
|
|
689
|
-
*/
|
|
690
|
-
function errorResult(error, partial) {
|
|
691
|
-
return withDrain({
|
|
692
|
-
error,
|
|
693
|
-
result: void 0,
|
|
694
|
-
command: partial?.command,
|
|
695
|
-
args: partial?.args,
|
|
696
|
-
argsResult: partial?.argsResult
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
/**
|
|
700
|
-
* Deduplicates plugins by `id`. When multiple plugins share the same `id`,
|
|
701
|
-
* only the last one in the array is kept. Plugins without an `id` are always kept.
|
|
702
|
-
*/
|
|
703
|
-
function deduplicatePlugins(plugins) {
|
|
704
|
-
if (!plugins.some((p) => p.id)) return plugins;
|
|
705
|
-
const lastIndex = /* @__PURE__ */ new Map();
|
|
706
|
-
for (let i = 0; i < plugins.length; i++) {
|
|
707
|
-
const id = plugins[i].id;
|
|
708
|
-
if (id) lastIndex.set(id, i);
|
|
709
|
-
}
|
|
710
|
-
return plugins.filter((p, i) => !p.id || lastIndex.get(p.id) === i);
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* Runs a plugin chain for a given phase using the onion/middleware pattern.
|
|
714
|
-
* Plugins are sorted by `order` (ascending, stable), then composed so that
|
|
715
|
-
* the first plugin in sorted order is the outermost wrapper.
|
|
716
|
-
* If no plugins handle this phase, `core` is called directly.
|
|
717
|
-
*/
|
|
718
|
-
function runPluginChain(phase, plugins, ctx, core) {
|
|
719
|
-
const phasePlugins = deduplicatePlugins(plugins).filter((p) => p[phase]);
|
|
720
|
-
if (phasePlugins.length === 0) return core();
|
|
721
|
-
phasePlugins.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
722
|
-
let next = core;
|
|
723
|
-
for (let i = phasePlugins.length - 1; i >= 0; i--) {
|
|
724
|
-
const handler = phasePlugins[i][phase];
|
|
725
|
-
const prevNext = next;
|
|
726
|
-
next = () => handler(ctx, prevNext);
|
|
727
|
-
}
|
|
728
|
-
return next();
|
|
729
|
-
}
|
|
730
|
-
/**
|
|
731
|
-
* Wraps a pipeline with start → error → shutdown lifecycle hooks.
|
|
732
|
-
* - `start` plugins wrap the pipeline (onion pattern, root plugins only).
|
|
733
|
-
* - On error: `error` plugins run (can transform/suppress the error).
|
|
734
|
-
* - Always: `shutdown` plugins run (success or failure).
|
|
735
|
-
*/
|
|
736
|
-
function wrapWithLifecycle(plugins, command, state, input, pipeline, wrapErrorResult) {
|
|
737
|
-
const hasStart = plugins.some((p) => p.start);
|
|
738
|
-
const hasError = plugins.some((p) => p.error);
|
|
739
|
-
const hasShutdown = plugins.some((p) => p.shutdown);
|
|
740
|
-
const cleanupProgress = (error, result) => {
|
|
741
|
-
const indicator = state._progress;
|
|
742
|
-
if (indicator) {
|
|
743
|
-
if (!("_progressMsg" in state)) indicator.stop();
|
|
744
|
-
else if (error !== void 0) {
|
|
745
|
-
const fallback = error instanceof Error ? error.message : String(error);
|
|
746
|
-
const { message: errorMsg, indicator: errorIcon } = resolveProgressMessage(state._progressError, error, fallback);
|
|
747
|
-
indicator.fail(errorMsg, errorIcon !== void 0 ? { indicator: errorIcon } : void 0);
|
|
748
|
-
} else {
|
|
749
|
-
const { message: successMsg, indicator: successIcon } = resolveProgressMessage(state._progressSuccess, result);
|
|
750
|
-
indicator.succeed(successMsg, successIcon !== void 0 ? { indicator: successIcon } : void 0);
|
|
751
|
-
}
|
|
752
|
-
state._restoreOutput?.();
|
|
753
|
-
state._progress = void 0;
|
|
754
|
-
state._restoreOutput = void 0;
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
if (!hasStart && !hasError && !hasShutdown) {
|
|
758
|
-
let result;
|
|
759
|
-
try {
|
|
760
|
-
result = pipeline();
|
|
761
|
-
} catch (e) {
|
|
762
|
-
cleanupProgress(e);
|
|
763
|
-
throw e;
|
|
764
|
-
}
|
|
765
|
-
if (result instanceof Promise) return result.then((r) => {
|
|
766
|
-
cleanupProgress();
|
|
767
|
-
return r;
|
|
768
|
-
}, (e) => {
|
|
769
|
-
cleanupProgress(e);
|
|
770
|
-
throw e;
|
|
771
|
-
});
|
|
772
|
-
cleanupProgress();
|
|
773
|
-
return result;
|
|
774
|
-
}
|
|
775
|
-
const runShutdown = (error, result) => {
|
|
776
|
-
cleanupProgress(error);
|
|
777
|
-
if (!hasShutdown) return;
|
|
778
|
-
return runPluginChain("shutdown", plugins, {
|
|
779
|
-
command,
|
|
780
|
-
state,
|
|
781
|
-
error,
|
|
782
|
-
result
|
|
783
|
-
}, () => {});
|
|
784
|
-
};
|
|
785
|
-
const runError = (error) => {
|
|
786
|
-
if (!hasError) {
|
|
787
|
-
const s = runShutdown(error);
|
|
788
|
-
if (s instanceof Promise) return s.then(() => {
|
|
789
|
-
throw error;
|
|
790
|
-
});
|
|
791
|
-
throw error;
|
|
792
|
-
}
|
|
793
|
-
return thenMaybe(runPluginChain("error", plugins, {
|
|
794
|
-
command,
|
|
795
|
-
state,
|
|
796
|
-
error
|
|
797
|
-
}, () => ({ error })), (er) => {
|
|
798
|
-
if (er.error !== void 0) return thenMaybe(runShutdown(er.error), () => {
|
|
799
|
-
throw er.error;
|
|
800
|
-
});
|
|
801
|
-
const wrapped = wrapErrorResult ? wrapErrorResult(er.result) : er.result;
|
|
802
|
-
return thenMaybe(runShutdown(void 0, wrapped), () => wrapped);
|
|
803
|
-
});
|
|
804
|
-
};
|
|
805
|
-
const handleSuccess = (result) => {
|
|
806
|
-
const s = runShutdown(void 0, result);
|
|
807
|
-
if (s instanceof Promise) return s.then(() => result);
|
|
808
|
-
return result;
|
|
809
|
-
};
|
|
810
|
-
const startCtx = {
|
|
811
|
-
command,
|
|
812
|
-
state,
|
|
813
|
-
input
|
|
814
|
-
};
|
|
815
|
-
let result;
|
|
816
|
-
try {
|
|
817
|
-
result = hasStart ? runPluginChain("start", plugins, startCtx, pipeline) : pipeline();
|
|
818
|
-
} catch (e) {
|
|
819
|
-
return runError(e);
|
|
820
|
-
}
|
|
821
|
-
if (result instanceof Promise) return result.then(handleSuccess, runError);
|
|
822
|
-
return handleSuccess(result);
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Resolves the runtime for a command by walking up the parent chain.
|
|
826
|
-
* Returns a fully resolved runtime with all defaults filled in.
|
|
827
|
-
*/
|
|
828
|
-
function getCommandRuntime(cmd) {
|
|
829
|
-
let current = cmd;
|
|
830
|
-
while (current) {
|
|
831
|
-
if (current.runtime) return resolveRuntime(current.runtime);
|
|
832
|
-
current = current.parent;
|
|
833
|
-
}
|
|
834
|
-
return resolveRuntime();
|
|
835
|
-
}
|
|
836
|
-
/** No-op progress indicator returned when the runtime doesn't provide a `progress` factory. */
|
|
837
|
-
const noopIndicator = {
|
|
838
|
-
update() {},
|
|
839
|
-
succeed() {},
|
|
840
|
-
fail() {},
|
|
841
|
-
stop() {},
|
|
842
|
-
pause() {},
|
|
843
|
-
resume() {}
|
|
844
|
-
};
|
|
845
|
-
/** Creates a progress indicator from the runtime, or returns a no-op if unavailable. */
|
|
846
|
-
function createProgress(runtime, message, options) {
|
|
847
|
-
return runtime.progress?.(message, options) ?? noopIndicator;
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Creates a lazy progress indicator that defers real indicator creation until first use.
|
|
851
|
-
* This allows `ctx.progress` to work even without `.progress()` config, as long as the
|
|
852
|
-
* runtime provides a progress factory.
|
|
853
|
-
*/
|
|
854
|
-
function createLazyIndicator(runtime, state) {
|
|
855
|
-
if (!runtime.progress) return noopIndicator;
|
|
856
|
-
let real;
|
|
857
|
-
const ensure = (message) => {
|
|
858
|
-
if (!real) {
|
|
859
|
-
real = runtime.progress(message ?? "", void 0);
|
|
860
|
-
state._progress = real;
|
|
861
|
-
}
|
|
862
|
-
return real;
|
|
863
|
-
};
|
|
864
|
-
return {
|
|
865
|
-
update(msg) {
|
|
866
|
-
ensure(msg).update(msg);
|
|
867
|
-
},
|
|
868
|
-
succeed(msg) {
|
|
869
|
-
if (real) real.succeed(msg);
|
|
870
|
-
},
|
|
871
|
-
fail(msg) {
|
|
872
|
-
if (real) real.fail(msg);
|
|
873
|
-
},
|
|
874
|
-
stop() {
|
|
875
|
-
if (real) real.stop();
|
|
876
|
-
},
|
|
877
|
-
pause() {
|
|
878
|
-
if (real) real.pause();
|
|
879
|
-
},
|
|
880
|
-
resume() {
|
|
881
|
-
if (real) real.resume();
|
|
882
|
-
}
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* Resolves a progress message field (static or callback) into the arguments for succeed/fail.
|
|
887
|
-
* Handles string, null, `{ message, indicator }` objects, and callback functions.
|
|
888
|
-
*/
|
|
889
|
-
function resolveProgressMessage(field, value, fallback) {
|
|
890
|
-
const raw = typeof field === "function" ? field(value) : field;
|
|
891
|
-
if (raw === void 0) return { message: fallback };
|
|
892
|
-
if (raw === null || typeof raw === "string") return { message: raw };
|
|
893
|
-
if (typeof raw === "object" && raw !== null) {
|
|
894
|
-
const obj = raw;
|
|
895
|
-
return {
|
|
896
|
-
message: obj.message,
|
|
897
|
-
indicator: obj.indicator
|
|
898
|
-
};
|
|
899
|
-
}
|
|
900
|
-
return { message: fallback };
|
|
901
|
-
}
|
|
902
|
-
function isAsyncBranded(schema) {
|
|
903
|
-
return !!schema && typeof schema === "object" && "~async" in schema && schema["~async"] === true;
|
|
904
|
-
}
|
|
905
|
-
function hasInteractiveConfig(meta) {
|
|
906
|
-
if (!meta || typeof meta !== "object") return false;
|
|
907
|
-
const m = meta;
|
|
908
|
-
return m.interactive === true || Array.isArray(m.interactive) || m.optionalInteractive === true || Array.isArray(m.optionalInteractive);
|
|
909
|
-
}
|
|
910
|
-
function warnIfUnexpectedAsync(value, command) {
|
|
911
|
-
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return value;
|
|
912
|
-
if (value instanceof Promise && !command.isAsync) getCommandRuntime(command).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.`);
|
|
913
|
-
return value;
|
|
914
|
-
}
|
|
915
|
-
/**
|
|
916
|
-
* Recursively re-paths a command tree under a new parent path, updating parent references.
|
|
917
|
-
*/
|
|
918
|
-
function repathCommandTree(cmd, newName, parentPath, parent) {
|
|
919
|
-
resolveCommand(cmd);
|
|
920
|
-
const newPath = parentPath ? `${parentPath} ${newName}` : newName;
|
|
921
|
-
const remounted = {
|
|
922
|
-
...cmd,
|
|
923
|
-
name: newName,
|
|
924
|
-
path: newPath,
|
|
925
|
-
parent,
|
|
926
|
-
version: void 0
|
|
927
|
-
};
|
|
928
|
-
if (cmd.commands?.length) remounted.commands = cmd.commands.map((child) => repathCommandTree(child, child.name, newPath, remounted));
|
|
929
|
-
return remounted;
|
|
930
|
-
}
|
|
931
|
-
/**
|
|
932
|
-
* Builds a completer function for the REPL from the command tree.
|
|
933
|
-
* Completes command names, subcommand names, option names (--foo), and aliases (-f).
|
|
934
|
-
* Also includes dot-prefixed built-in REPL commands (.exit, .clear, .scope, .help, .history).
|
|
935
|
-
*/
|
|
936
|
-
function buildReplCompleter(rootCommand, builtins) {
|
|
937
|
-
resolveAllCommands(rootCommand);
|
|
938
|
-
return (line) => {
|
|
939
|
-
const parts = line.trimStart().split(/\s+/);
|
|
940
|
-
const lastPart = parts[parts.length - 1] ?? "";
|
|
941
|
-
if (lastPart.startsWith(".")) {
|
|
942
|
-
const dotCmds = [
|
|
943
|
-
".exit",
|
|
944
|
-
".clear",
|
|
945
|
-
".help",
|
|
946
|
-
".history"
|
|
947
|
-
];
|
|
948
|
-
if (rootCommand.commands?.some((c) => c.commands?.length) || builtins.inScope) dotCmds.push(".scope");
|
|
949
|
-
const hits = dotCmds.filter((c) => c.startsWith(lastPart));
|
|
950
|
-
return [hits.length ? hits : dotCmds, lastPart];
|
|
951
|
-
}
|
|
952
|
-
if (lastPart.startsWith("-")) {
|
|
953
|
-
const commandParts = parts.slice(0, -1).filter((p) => !p.startsWith("-"));
|
|
954
|
-
let targetCommand = rootCommand;
|
|
955
|
-
for (const part of commandParts) {
|
|
956
|
-
resolveCommand(targetCommand);
|
|
957
|
-
const sub = targetCommand.commands?.find((c) => c.name === part || c.aliases?.includes(part));
|
|
958
|
-
if (sub) {
|
|
959
|
-
resolveCommand(sub);
|
|
960
|
-
targetCommand = sub;
|
|
961
|
-
} else break;
|
|
962
|
-
}
|
|
963
|
-
const options = [];
|
|
964
|
-
if (targetCommand.argsSchema) try {
|
|
965
|
-
const argsMeta = targetCommand.meta?.fields;
|
|
966
|
-
const { flags, aliases } = extractSchemaMetadata(targetCommand.argsSchema, argsMeta, targetCommand.meta?.autoAlias);
|
|
967
|
-
const jsonSchema = targetCommand.argsSchema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS);
|
|
968
|
-
if (jsonSchema.type === "object" && jsonSchema.properties) {
|
|
969
|
-
for (const key of Object.keys(jsonSchema.properties)) options.push(`--${key}`);
|
|
970
|
-
for (const flag of Object.keys(flags)) options.push(`-${flag}`);
|
|
971
|
-
for (const alias of Object.keys(aliases)) options.push(`--${alias}`);
|
|
972
|
-
}
|
|
973
|
-
} catch {}
|
|
974
|
-
options.push("--help", "-h");
|
|
975
|
-
const hits = options.filter((o) => o.startsWith(lastPart));
|
|
976
|
-
return [hits.length ? hits : options, lastPart];
|
|
977
|
-
}
|
|
978
|
-
const commandParts = parts.filter((p) => !p.startsWith("-"));
|
|
979
|
-
let targetCommand = rootCommand;
|
|
980
|
-
for (let i = 0; i < commandParts.length - 1; i++) {
|
|
981
|
-
resolveCommand(targetCommand);
|
|
982
|
-
const sub = targetCommand.commands?.find((c) => c.name === commandParts[i] || c.aliases?.includes(commandParts[i]));
|
|
983
|
-
if (sub) {
|
|
984
|
-
resolveCommand(sub);
|
|
985
|
-
targetCommand = sub;
|
|
986
|
-
} else break;
|
|
987
|
-
}
|
|
988
|
-
const candidates = [];
|
|
989
|
-
if (targetCommand.commands) {
|
|
990
|
-
for (const cmd of targetCommand.commands) if (!cmd.hidden) {
|
|
991
|
-
candidates.push(cmd.name);
|
|
992
|
-
if (cmd.aliases) candidates.push(...cmd.aliases);
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
if (targetCommand === rootCommand) {
|
|
996
|
-
candidates.push(".help", ".exit", ".clear", ".history");
|
|
997
|
-
if (rootCommand.commands?.some((c) => c.commands?.length) || builtins.inScope) candidates.push(".scope");
|
|
998
|
-
if (builtins.inScope) candidates.push("..");
|
|
999
|
-
}
|
|
1000
|
-
const hits = candidates.filter((c) => c.startsWith(lastPart));
|
|
1001
|
-
return [hits.length ? hits : candidates, lastPart];
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
/**
|
|
1005
|
-
* Computes the Levenshtein edit distance between two strings.
|
|
1006
|
-
*/
|
|
1007
|
-
function levenshtein(a, b) {
|
|
1008
|
-
const m = a.length;
|
|
1009
|
-
const n = b.length;
|
|
1010
|
-
const dp = Array.from({ length: n + 1 }, (_, i) => i);
|
|
1011
|
-
for (let i = 1; i <= m; i++) {
|
|
1012
|
-
let prev = dp[0];
|
|
1013
|
-
dp[0] = i;
|
|
1014
|
-
for (let j = 1; j <= n; j++) {
|
|
1015
|
-
const temp = dp[j];
|
|
1016
|
-
dp[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, dp[j], dp[j - 1]);
|
|
1017
|
-
prev = temp;
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
return dp[n];
|
|
1021
|
-
}
|
|
1022
|
-
/**
|
|
1023
|
-
* Finds the closest match from a list of candidates using Levenshtein distance.
|
|
1024
|
-
* Returns the suggestion string (e.g. 'Did you mean "deploy"?') or empty string if no good match.
|
|
1025
|
-
* Threshold: distance must be at most 40% of the longer string's length (min 1, max 3).
|
|
1026
|
-
*/
|
|
1027
|
-
function suggestSimilar(input, candidates) {
|
|
1028
|
-
if (candidates.length === 0) return "";
|
|
1029
|
-
const lower = input.toLowerCase();
|
|
1030
|
-
let bestDist = Infinity;
|
|
1031
|
-
let bestMatch = "";
|
|
1032
|
-
for (const candidate of candidates) {
|
|
1033
|
-
const dist = levenshtein(lower, candidate.toLowerCase());
|
|
1034
|
-
if (dist < bestDist) {
|
|
1035
|
-
bestDist = dist;
|
|
1036
|
-
bestMatch = candidate;
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
const maxLen = Math.max(input.length, bestMatch.length);
|
|
1040
|
-
const threshold = Math.min(3, Math.max(1, Math.ceil(maxLen * .4)));
|
|
1041
|
-
if (bestDist > 0 && bestDist <= threshold) return `Did you mean "${bestMatch}"?`;
|
|
1042
|
-
return "";
|
|
1043
|
-
}
|
|
1044
|
-
function findCommandByName(name, commands) {
|
|
1045
|
-
if (!commands) return void 0;
|
|
1046
|
-
const foundByName = commands.find((cmd) => cmd.name === name);
|
|
1047
|
-
if (foundByName) return resolveCommand(foundByName);
|
|
1048
|
-
const foundByAlias = commands.find((cmd) => cmd.aliases?.includes(name));
|
|
1049
|
-
if (foundByAlias) return resolveCommand(foundByAlias);
|
|
1050
|
-
for (const cmd of commands) {
|
|
1051
|
-
if (name.startsWith(`${cmd.name} `)) {
|
|
1052
|
-
resolveCommand(cmd);
|
|
1053
|
-
if (cmd.commands) {
|
|
1054
|
-
const subCommand = findCommandByName(name.slice(cmd.name.length + 1), cmd.commands);
|
|
1055
|
-
if (subCommand) return subCommand;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
if (cmd.aliases) {
|
|
1059
|
-
for (const alias of cmd.aliases) if (name.startsWith(`${alias} `)) {
|
|
1060
|
-
resolveCommand(cmd);
|
|
1061
|
-
if (cmd.commands) {
|
|
1062
|
-
const subCommand = findCommandByName(name.slice(alias.length + 1), cmd.commands);
|
|
1063
|
-
if (subCommand) return subCommand;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
/** Collect all actionable commands recursively. Hidden commands are excluded. */
|
|
1070
|
-
function collectEndpoints(commands, prefix) {
|
|
1071
|
-
if (!commands) return [];
|
|
1072
|
-
const endpoints = [];
|
|
1073
|
-
for (const cmd of commands) {
|
|
1074
|
-
resolveCommand(cmd);
|
|
1075
|
-
if (cmd.hidden) continue;
|
|
1076
|
-
const path = cmd.name ? prefix ? `${prefix}.${cmd.name}` : cmd.name : prefix;
|
|
1077
|
-
if (cmd.action || cmd.argsSchema) endpoints.push({
|
|
1078
|
-
name: path,
|
|
1079
|
-
command: cmd
|
|
1080
|
-
});
|
|
1081
|
-
if (cmd.commands?.length) endpoints.push(...collectEndpoints(cmd.commands, path));
|
|
1082
|
-
}
|
|
1083
|
-
return endpoints;
|
|
1084
|
-
}
|
|
1085
|
-
/** Build the JSON Schema for a command's arguments. */
|
|
1086
|
-
function buildInputSchema(cmd) {
|
|
1087
|
-
if (!cmd.argsSchema) return {
|
|
1088
|
-
type: "object",
|
|
1089
|
-
additionalProperties: false
|
|
1090
|
-
};
|
|
1091
|
-
try {
|
|
1092
|
-
return cmd.argsSchema["~standard"].jsonSchema.input(JSON_SCHEMA_OPTS);
|
|
1093
|
-
} catch {
|
|
1094
|
-
return {
|
|
1095
|
-
type: "object",
|
|
1096
|
-
additionalProperties: false
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
/** Serialize a record of args into CLI flag strings. */
|
|
1101
|
-
function serializeArgsToFlags(args) {
|
|
1102
|
-
const parts = [];
|
|
1103
|
-
for (const [key, value] of Object.entries(args)) {
|
|
1104
|
-
if (value === void 0) continue;
|
|
1105
|
-
if (typeof value === "boolean") parts.push(value ? `--${key}` : `--no-${key}`);
|
|
1106
|
-
else if (Array.isArray(value)) for (const v of value) parts.push(`--${key}=${String(v)}`);
|
|
1107
|
-
else {
|
|
1108
|
-
const strVal = String(value);
|
|
1109
|
-
parts.push(strVal.includes(" ") ? `--${key}="${strVal}"` : `--${key}=${strVal}`);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
return parts;
|
|
1113
|
-
}
|
|
1114
|
-
//#endregion
|
|
1115
|
-
export { withDrain as A, resolveCommand as C, suggestSimilar as D, serializeArgsToFlags as E, resolveStdin as F, resolveStdinAlways as I, getRootCommand as L, wrapWithLifecycle as M, REPL_SIGINT as N, thenMaybe as O, createTerminalReplSession as P, getVersion as R, resolveAllCommands as S, runPluginChain as T, mergeCommands as _, commandSymbol as a, outputValue as b, createProgress as c, getCommand as d, getCommandRuntime as f, makeThenable as g, lazyResolver as h, collectEndpoints as i, withPromiseDrain as j, warnIfUnexpectedAsync as k, errorResult as l, isAsyncBranded as m, buildInputSchema as n, command_utils_exports as o, hasInteractiveConfig as p, buildReplCompleter as r, createLazyIndicator as s, asyncSchema as t, findCommandByName as u, noop as v, resolveProgressMessage as w, repathCommandTree as x, noopIndicator as y };
|
|
1116
|
-
|
|
1117
|
-
//# sourceMappingURL=command-utils-B1D-HqCd.mjs.map
|