hacksh 0.12.0 → 0.14.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/README.md +123 -92
- package/dist/command-CfWrbHLN.d.ts +378 -0
- package/dist/index.d.ts +17 -402
- package/dist/index.js +139 -1146
- package/dist/index.js.map +1 -1
- package/dist/pretty-BFub611x.js +1012 -0
- package/dist/pretty-BFub611x.js.map +1 -0
- package/dist/printer-api.d.ts +13 -0
- package/dist/printer-api.js +2 -0
- package/package.json +5 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { a as createExec, c as HackshPromptError, d as summarizeError, f as summarizeErrors, i as resolveOutputMode, l as HackshUsageError, n as createJsonPrinter, o as HackshExecError, r as createPrintQueue, s as HackshExitError, t as createPrettyPrinter, u as HackshValidationError } from "./pretty-BFub611x.js";
|
|
1
2
|
import { globalRegistry, safeParseAsync } from "zod/v4/core";
|
|
2
|
-
import {
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
4
|
-
import { emitKeypressEvents } from "node:readline";
|
|
5
|
-
import { PassThrough } from "node:stream";
|
|
3
|
+
import { parseArgs as parseArgs$1 } from "node:util";
|
|
6
4
|
import { readFile, realpath } from "node:fs/promises";
|
|
7
5
|
import { dirname, join } from "node:path";
|
|
6
|
+
import { emitKeypressEvents } from "node:readline";
|
|
7
|
+
import { PassThrough } from "node:stream";
|
|
8
8
|
//#region src/options.ts
|
|
9
9
|
function kebabCase(value) {
|
|
10
10
|
return value.replaceAll(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
@@ -12,6 +12,9 @@ function kebabCase(value) {
|
|
|
12
12
|
function aliasList(value) {
|
|
13
13
|
return typeof value === "string" ? [value] : value;
|
|
14
14
|
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/schema.ts
|
|
17
|
+
const POSITIONALS_KEY = "positionals";
|
|
15
18
|
function definition(schema) {
|
|
16
19
|
return schema._zod.def;
|
|
17
20
|
}
|
|
@@ -35,8 +38,23 @@ function readArgInfo(schema) {
|
|
|
35
38
|
let defaultValue;
|
|
36
39
|
let description;
|
|
37
40
|
let title;
|
|
41
|
+
let alias;
|
|
42
|
+
let valueName;
|
|
43
|
+
let env;
|
|
38
44
|
while (current) {
|
|
39
45
|
const metadata = globalRegistry.get(current);
|
|
46
|
+
if (metadata) {
|
|
47
|
+
if (metadata.alias !== void 0 && typeof metadata.alias !== "string" && !(Array.isArray(metadata.alias) && metadata.alias.every((value) => typeof value === "string"))) throw new TypeError("Argument metadata `alias` must be a string or an array of strings");
|
|
48
|
+
if (metadata.env !== void 0 && typeof metadata.env !== "string" && metadata.env !== false) throw new TypeError("Argument metadata `env` must be a string or false");
|
|
49
|
+
for (const key of [
|
|
50
|
+
"valueName",
|
|
51
|
+
"title",
|
|
52
|
+
"description"
|
|
53
|
+
]) if (metadata[key] !== void 0 && typeof metadata[key] !== "string") throw new TypeError(`Argument metadata \`${key}\` must be a string`);
|
|
54
|
+
alias ??= metadata.alias;
|
|
55
|
+
valueName ??= metadata.valueName;
|
|
56
|
+
env ??= metadata.env;
|
|
57
|
+
}
|
|
40
58
|
description ??= metadata?.description;
|
|
41
59
|
title ??= metadata?.title;
|
|
42
60
|
const def = definition(current);
|
|
@@ -51,12 +69,15 @@ function readArgInfo(schema) {
|
|
|
51
69
|
defaultValue,
|
|
52
70
|
description,
|
|
53
71
|
title,
|
|
72
|
+
alias,
|
|
73
|
+
valueName,
|
|
74
|
+
env,
|
|
54
75
|
choices: enumChoices(base)
|
|
55
76
|
};
|
|
56
77
|
}
|
|
57
|
-
/** Read the `
|
|
78
|
+
/** Read the `positionals` tuple or array schema that declares positional arguments. */
|
|
58
79
|
function readPositionals(shape) {
|
|
59
|
-
const field = shape[
|
|
80
|
+
const field = shape[POSITIONALS_KEY];
|
|
60
81
|
if (!field) return void 0;
|
|
61
82
|
const def = definition(unwrapInput(field));
|
|
62
83
|
let items;
|
|
@@ -67,7 +88,7 @@ function readPositionals(shape) {
|
|
|
67
88
|
} else if (def.type === "array") {
|
|
68
89
|
items = [];
|
|
69
90
|
rest = def.element;
|
|
70
|
-
} else throw new TypeError("Positional arguments `
|
|
91
|
+
} else throw new TypeError("Positional arguments `positionals` must use a tuple or array schema");
|
|
71
92
|
return {
|
|
72
93
|
items: items.map((schema, index) => ({
|
|
73
94
|
schema,
|
|
@@ -115,24 +136,31 @@ function defineCommand(def) {
|
|
|
115
136
|
let sawOptional = false;
|
|
116
137
|
for (const { schema, label } of positionals.items) {
|
|
117
138
|
const info = readArgInfo(schema);
|
|
139
|
+
if (info.alias !== void 0 || info.env !== void 0 || info.valueName !== void 0) throw new TypeError(`Positional argument \`${label}\` cannot have alias, env, or valueName metadata`);
|
|
118
140
|
if (isArrayInput(schema)) throw new TypeError(`Positional argument \`${label}\` cannot be an array; use a tuple rest`);
|
|
119
141
|
if (!info.takesValue) throw new TypeError(`Boolean argument \`${label}\` cannot be positional`);
|
|
120
142
|
if (sawOptional && info.required) throw new TypeError(`Required positional \`${label}\` cannot follow an optional positional`);
|
|
121
143
|
sawOptional ||= !info.required;
|
|
122
144
|
}
|
|
145
|
+
if (positionals.rest) {
|
|
146
|
+
const info = readArgInfo(positionals.rest.schema);
|
|
147
|
+
if (info.alias !== void 0 || info.env !== void 0 || info.valueName !== void 0) throw new TypeError(`Positional argument \`${positionals.rest.label}\` cannot have alias, env, or valueName metadata`);
|
|
148
|
+
}
|
|
123
149
|
if (def.subCommands && Object.keys(def.subCommands).length > 0) throw new TypeError("A command cannot define both positional arguments and subcommands");
|
|
124
150
|
}
|
|
125
151
|
const usedAliases = /* @__PURE__ */ new Set();
|
|
126
|
-
for (const [key,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
for (const [key, schema] of Object.entries(shape)) {
|
|
153
|
+
const options = readArgInfo(schema);
|
|
154
|
+
if (key === "positionals") {
|
|
155
|
+
if (options.alias !== void 0 || options.env !== void 0 || options.valueName !== void 0) throw new TypeError("Positional arguments cannot have alias, env, or valueName metadata");
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (options.env !== void 0) {
|
|
131
159
|
if (options.env === "") throw new TypeError(`Environment variable name for \`${key}\` cannot be empty`);
|
|
132
160
|
if (isArrayInput(schema)) throw new TypeError(`Option \`${key}\` cannot use an environment variable because it accepts an array`);
|
|
133
161
|
if (isObjectInput(schema)) throw new TypeError(`Option \`${key}\` cannot use an environment variable because it accepts an object`);
|
|
134
162
|
}
|
|
135
|
-
if (options
|
|
163
|
+
if (options.alias === void 0) continue;
|
|
136
164
|
for (const alias of aliasList(options.alias)) {
|
|
137
165
|
if (alias.length !== 1) throw new TypeError(`Alias \`${alias}\` must be exactly one character`);
|
|
138
166
|
if (usedAliases.has(alias)) throw new TypeError(`Duplicate alias \`${alias}\``);
|
|
@@ -140,228 +168,12 @@ function defineCommand(def) {
|
|
|
140
168
|
}
|
|
141
169
|
}
|
|
142
170
|
for (const key of Object.keys(shape)) {
|
|
143
|
-
if (key === "help") throw new TypeError(
|
|
144
|
-
if (key !== "
|
|
171
|
+
if (key === "help" || key === "version") throw new TypeError(`Option \`${key}\` conflicts with the builtin --${key}`);
|
|
172
|
+
if (key !== "positionals" && /^no[-A-Z]/.test(key)) throw new TypeError(`Option \`${key}\` conflicts with --no- negation`);
|
|
145
173
|
}
|
|
146
174
|
return def;
|
|
147
175
|
}
|
|
148
176
|
//#endregion
|
|
149
|
-
//#region src/errors.ts
|
|
150
|
-
var HackshUsageError = class extends Error {
|
|
151
|
-
command;
|
|
152
|
-
constructor(message, command, options) {
|
|
153
|
-
super(message, options);
|
|
154
|
-
this.name = "HackshUsageError";
|
|
155
|
-
this.command = command;
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
var HackshValidationError = class extends HackshUsageError {
|
|
159
|
-
issues;
|
|
160
|
-
constructor(command, issues) {
|
|
161
|
-
super(`Invalid arguments:\n${issues.map(({ arg, message }) => ` ${arg}: ${message}`).join("\n")}`, command);
|
|
162
|
-
this.name = "HackshValidationError";
|
|
163
|
-
this.issues = issues;
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
/** An input request that could not be completed safely. */
|
|
167
|
-
var HackshPromptError = class extends Error {
|
|
168
|
-
reason;
|
|
169
|
-
prompt;
|
|
170
|
-
constructor(reason, prompt, message, options) {
|
|
171
|
-
super(message, options);
|
|
172
|
-
this.name = "HackshPromptError";
|
|
173
|
-
this.reason = reason;
|
|
174
|
-
this.prompt = prompt;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
/** An external command that did not succeed; `result` is not enumerable so error summaries omit child output. */
|
|
178
|
-
var HackshExecError = class extends Error {
|
|
179
|
-
#result;
|
|
180
|
-
constructor(message, result, options) {
|
|
181
|
-
super(message, options);
|
|
182
|
-
this.name = "HackshExecError";
|
|
183
|
-
this.#result = result;
|
|
184
|
-
}
|
|
185
|
-
get result() {
|
|
186
|
-
return this.#result;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
/** A requested process exit status; `runMain` exits with it and reports only a non-empty message. */
|
|
190
|
-
var HackshExitError = class extends Error {
|
|
191
|
-
exitCode;
|
|
192
|
-
constructor(exitCode, message, options) {
|
|
193
|
-
if (!Number.isInteger(exitCode) || exitCode < 0 || exitCode > 255) throw new TypeError("Exit code must be an integer from 0 to 255");
|
|
194
|
-
super(message, options);
|
|
195
|
-
this.name = "HackshExitError";
|
|
196
|
-
this.exitCode = exitCode;
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
function safeString$1(value, fallback) {
|
|
200
|
-
try {
|
|
201
|
-
return String(value);
|
|
202
|
-
} catch {
|
|
203
|
-
return fallback;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function readProperty(value, key) {
|
|
207
|
-
try {
|
|
208
|
-
return Reflect.get(value, key);
|
|
209
|
-
} catch {
|
|
210
|
-
return "[Unserializable]";
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
function enumerableKeys(value) {
|
|
214
|
-
try {
|
|
215
|
-
return Object.keys(value);
|
|
216
|
-
} catch {
|
|
217
|
-
return [];
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function hasToJSON(value) {
|
|
221
|
-
try {
|
|
222
|
-
return typeof Reflect.get(value, "toJSON") === "function";
|
|
223
|
-
} catch {
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function isPlainObject(value) {
|
|
228
|
-
try {
|
|
229
|
-
const prototype = Object.getPrototypeOf(value);
|
|
230
|
-
return prototype === Object.prototype || prototype === null;
|
|
231
|
-
} catch {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function summarizeIssue({ arg, message, issue }, seen) {
|
|
236
|
-
if (!issue) return {
|
|
237
|
-
arg,
|
|
238
|
-
message
|
|
239
|
-
};
|
|
240
|
-
const details = {};
|
|
241
|
-
for (const key of enumerableKeys(issue)) {
|
|
242
|
-
if ([
|
|
243
|
-
"code",
|
|
244
|
-
"path",
|
|
245
|
-
"message",
|
|
246
|
-
"inst"
|
|
247
|
-
].includes(key)) continue;
|
|
248
|
-
details[key] = summarizeErrors(readProperty(issue, key), seen);
|
|
249
|
-
}
|
|
250
|
-
const code = readProperty(issue, "code");
|
|
251
|
-
const path = readProperty(issue, "path");
|
|
252
|
-
return {
|
|
253
|
-
arg,
|
|
254
|
-
message,
|
|
255
|
-
...typeof code === "string" ? { code } : {},
|
|
256
|
-
...Array.isArray(path) ? { path } : {},
|
|
257
|
-
...Object.keys(details).length === 0 ? {} : { details }
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
function errorKind(error) {
|
|
261
|
-
if (error instanceof HackshPromptError) return "prompt";
|
|
262
|
-
if (error instanceof HackshValidationError) return "validation";
|
|
263
|
-
if (error instanceof HackshUsageError) return "usage";
|
|
264
|
-
return "error";
|
|
265
|
-
}
|
|
266
|
-
function summarizeErrorWithSeen(error, seen) {
|
|
267
|
-
const summary = {
|
|
268
|
-
kind: errorKind(error),
|
|
269
|
-
name: safeString$1(readProperty(error, "name"), "Error"),
|
|
270
|
-
message: safeString$1(readProperty(error, "message"), "[Unserializable error message]")
|
|
271
|
-
};
|
|
272
|
-
seen.set(error, summary);
|
|
273
|
-
const stack = readProperty(error, "stack");
|
|
274
|
-
const cause = readProperty(error, "cause");
|
|
275
|
-
if (typeof stack === "string") summary.stack = stack;
|
|
276
|
-
if (cause !== void 0) summary.cause = summarizeErrors(cause, seen);
|
|
277
|
-
if (error instanceof HackshValidationError) summary.issues = error.issues.map((issue) => summarizeIssue(issue, seen));
|
|
278
|
-
const reserved = /* @__PURE__ */ new Set([
|
|
279
|
-
"name",
|
|
280
|
-
"message",
|
|
281
|
-
"stack",
|
|
282
|
-
"cause",
|
|
283
|
-
"command",
|
|
284
|
-
"issues"
|
|
285
|
-
]);
|
|
286
|
-
const properties = {};
|
|
287
|
-
for (const key of enumerableKeys(error)) {
|
|
288
|
-
if (reserved.has(key)) continue;
|
|
289
|
-
properties[key] = summarizeErrors(readProperty(error, key), seen);
|
|
290
|
-
}
|
|
291
|
-
if (Object.keys(properties).length > 0) summary.properties = properties;
|
|
292
|
-
return summary;
|
|
293
|
-
}
|
|
294
|
-
/** Recursively replace Error values with command- and schema-free summaries. */
|
|
295
|
-
function summarizeErrors(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
296
|
-
if (typeof value !== "object" || value === null) return value;
|
|
297
|
-
const existing = seen.get(value);
|
|
298
|
-
if (existing !== void 0) return existing;
|
|
299
|
-
if (value instanceof Error) return summarizeErrorWithSeen(value, seen);
|
|
300
|
-
if (Array.isArray(value)) {
|
|
301
|
-
const result = [];
|
|
302
|
-
seen.set(value, result);
|
|
303
|
-
let changed = false;
|
|
304
|
-
for (const item of value) {
|
|
305
|
-
const summarized = summarizeErrors(item, seen);
|
|
306
|
-
result.push(summarized);
|
|
307
|
-
changed ||= summarized !== item;
|
|
308
|
-
}
|
|
309
|
-
if (changed) return result;
|
|
310
|
-
seen.set(value, value);
|
|
311
|
-
return value;
|
|
312
|
-
}
|
|
313
|
-
if (value instanceof Map) {
|
|
314
|
-
const result = /* @__PURE__ */ new Map();
|
|
315
|
-
seen.set(value, result);
|
|
316
|
-
let changed = false;
|
|
317
|
-
for (const [key, item] of value) {
|
|
318
|
-
const summarizedKey = summarizeErrors(key, seen);
|
|
319
|
-
const summarizedItem = summarizeErrors(item, seen);
|
|
320
|
-
result.set(summarizedKey, summarizedItem);
|
|
321
|
-
changed ||= summarizedKey !== key || summarizedItem !== item;
|
|
322
|
-
}
|
|
323
|
-
if (changed) return result;
|
|
324
|
-
seen.set(value, value);
|
|
325
|
-
return value;
|
|
326
|
-
}
|
|
327
|
-
if (value instanceof Set) {
|
|
328
|
-
const result = /* @__PURE__ */ new Set();
|
|
329
|
-
seen.set(value, result);
|
|
330
|
-
let changed = false;
|
|
331
|
-
for (const item of value) {
|
|
332
|
-
const summarized = summarizeErrors(item, seen);
|
|
333
|
-
result.add(summarized);
|
|
334
|
-
changed ||= summarized !== item;
|
|
335
|
-
}
|
|
336
|
-
if (changed) return result;
|
|
337
|
-
seen.set(value, value);
|
|
338
|
-
return value;
|
|
339
|
-
}
|
|
340
|
-
if (ArrayBuffer.isView(value) || hasToJSON(value) || !isPlainObject(value)) return value;
|
|
341
|
-
const result = {};
|
|
342
|
-
seen.set(value, result);
|
|
343
|
-
let changed = false;
|
|
344
|
-
for (const key of enumerableKeys(value)) {
|
|
345
|
-
const original = readProperty(value, key);
|
|
346
|
-
const summarized = summarizeErrors(original, seen);
|
|
347
|
-
result[key] = summarized;
|
|
348
|
-
changed ||= summarized !== original;
|
|
349
|
-
}
|
|
350
|
-
if (changed) return result;
|
|
351
|
-
seen.set(value, value);
|
|
352
|
-
return value;
|
|
353
|
-
}
|
|
354
|
-
/** Convert any thrown value into a canonical error description. */
|
|
355
|
-
function summarizeError(error) {
|
|
356
|
-
if (error instanceof Error) return summarizeErrorWithSeen(error, /* @__PURE__ */ new WeakMap());
|
|
357
|
-
return {
|
|
358
|
-
kind: "thrown",
|
|
359
|
-
name: "NonErrorThrown",
|
|
360
|
-
message: safeString$1(error, "[Unserializable thrown value]"),
|
|
361
|
-
value: summarizeErrors(error)
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
//#endregion
|
|
365
177
|
//#region src/environment.ts
|
|
366
178
|
const commandPaths = /* @__PURE__ */ new WeakMap();
|
|
367
179
|
function registerCommandPath(command, path) {
|
|
@@ -378,9 +190,9 @@ function optionEnvNames(command, path) {
|
|
|
378
190
|
const names = /* @__PURE__ */ new Map();
|
|
379
191
|
if (!command.args) return names;
|
|
380
192
|
for (const [key, schema] of Object.entries(schemaShape(command.args))) {
|
|
381
|
-
if (key === "
|
|
193
|
+
if (key === "positionals") continue;
|
|
382
194
|
if (isArrayInput(schema) || isObjectInput(schema)) continue;
|
|
383
|
-
const configured =
|
|
195
|
+
const configured = readArgInfo(schema).env;
|
|
384
196
|
if (configured === false) continue;
|
|
385
197
|
if (typeof configured === "string") {
|
|
386
198
|
names.set(key, configured);
|
|
@@ -420,13 +232,13 @@ function optionModel(command) {
|
|
|
420
232
|
const parserOptions = {};
|
|
421
233
|
const allAliases = /* @__PURE__ */ new Set();
|
|
422
234
|
if (command.args) for (const [key, schema] of Object.entries(schemaShape(command.args))) {
|
|
423
|
-
if (key === "
|
|
424
|
-
const
|
|
425
|
-
const aliases = alias ? aliasList(alias)
|
|
235
|
+
if (key === "positionals") continue;
|
|
236
|
+
const info = readArgInfo(schema);
|
|
237
|
+
const aliases = info.alias === void 0 ? [] : aliasList(info.alias);
|
|
426
238
|
const definition = {
|
|
427
239
|
key,
|
|
428
240
|
kebabName: kebabCase(key),
|
|
429
|
-
takesValue:
|
|
241
|
+
takesValue: info.takesValue
|
|
430
242
|
};
|
|
431
243
|
const type = definition.takesValue ? "string" : "boolean";
|
|
432
244
|
for (const name of /* @__PURE__ */ new Set([key, definition.kebabName])) {
|
|
@@ -454,7 +266,7 @@ function commandPositionals(command) {
|
|
|
454
266
|
function displayName(command, path) {
|
|
455
267
|
const [first, index] = path;
|
|
456
268
|
if (typeof first !== "string") return "arguments";
|
|
457
|
-
if (first !== "
|
|
269
|
+
if (first !== "positionals") return `--${kebabCase(first)}`;
|
|
458
270
|
const positionals = commandPositionals(command);
|
|
459
271
|
if (!positionals || typeof index !== "number") return "arguments";
|
|
460
272
|
const item = positionals.items[index];
|
|
@@ -606,7 +418,7 @@ async function parseCommandArgs(command, rawArgs, tokenized = tokenizeCommandArg
|
|
|
606
418
|
const input = {};
|
|
607
419
|
const positionals = commandPositionals(command);
|
|
608
420
|
const positionalValues = [];
|
|
609
|
-
if (positionals) input[
|
|
421
|
+
if (positionals) input[POSITIONALS_KEY] = positionalValues;
|
|
610
422
|
const extraArgs = [];
|
|
611
423
|
for (const token of tokenized.tokens) if (token.kind === "known-option") input[token.definition.key] = token.value;
|
|
612
424
|
else if (token.kind === "unknown-option") extraArgs.push(token.value);
|
|
@@ -679,803 +491,91 @@ var TerminalLock = class {
|
|
|
679
491
|
}
|
|
680
492
|
};
|
|
681
493
|
//#endregion
|
|
682
|
-
//#region src/
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
"maxBuffer",
|
|
691
|
-
"forceKillAfterDelay",
|
|
692
|
-
"stdin",
|
|
693
|
-
"stdout",
|
|
694
|
-
"stderr",
|
|
695
|
-
"interactive",
|
|
696
|
-
"display",
|
|
697
|
-
"redact"
|
|
698
|
-
]);
|
|
699
|
-
const MAX_DELAY = 2147483647;
|
|
700
|
-
const REFUSAL_MESSAGE = {
|
|
701
|
-
"json-output": "Command cannot use the terminal in JSON output mode",
|
|
702
|
-
"non-interactive": "Command requires an interactive terminal",
|
|
703
|
-
overlapping: "Command cannot use the terminal while it is in use"
|
|
704
|
-
};
|
|
705
|
-
function isRecord(value) {
|
|
706
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
707
|
-
}
|
|
708
|
-
function assertNoNul(value, label) {
|
|
709
|
-
if (value.includes("\0")) throw new TypeError(`${label} must not contain NUL characters`);
|
|
710
|
-
}
|
|
711
|
-
function readInteger(value, label, minimum, maximum) {
|
|
712
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) throw new TypeError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
713
|
-
return value;
|
|
714
|
-
}
|
|
715
|
-
function readEnum(value, label, allowed) {
|
|
716
|
-
if (typeof value === "string" && allowed.includes(value)) return value;
|
|
717
|
-
throw new TypeError(`Exec option \`${label}\` must be ${allowed.map((item) => JSON.stringify(item)).join(" or ")}`);
|
|
718
|
-
}
|
|
719
|
-
function readBoolean(value, label) {
|
|
720
|
-
if (typeof value !== "boolean") throw new TypeError(`Exec option \`${label}\` must be a boolean`);
|
|
721
|
-
return value;
|
|
722
|
-
}
|
|
723
|
-
function readRedact(value) {
|
|
724
|
-
if (!Array.isArray(value)) throw new TypeError("Exec option `redact` must be an array");
|
|
725
|
-
for (const item of value) {
|
|
726
|
-
if (typeof item !== "string" || item === "") throw new TypeError("Exec option `redact` must contain only non-empty strings");
|
|
727
|
-
assertNoNul(item, "Exec option `redact`");
|
|
728
|
-
}
|
|
729
|
-
return value;
|
|
730
|
-
}
|
|
731
|
-
function mergeEnvironment(base, overrides) {
|
|
732
|
-
if (overrides === void 0 && base === void 0) return void 0;
|
|
733
|
-
if (overrides !== void 0 && !isRecord(overrides)) throw new TypeError("Exec option `env` must be an object");
|
|
734
|
-
const merged = {};
|
|
735
|
-
for (const [key, value] of Object.entries(base ?? process.env)) if (value !== void 0) merged[key] = value;
|
|
736
|
-
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
737
|
-
if (key === "" || key.includes("=") || key.includes("\0")) throw new TypeError(`Invalid environment variable name ${JSON.stringify(key)}`);
|
|
738
|
-
if (value === void 0) {
|
|
739
|
-
delete merged[key];
|
|
740
|
-
continue;
|
|
741
|
-
}
|
|
742
|
-
if (typeof value !== "string") throw new TypeError(`Environment variable ${key} must be a string or undefined`);
|
|
743
|
-
assertNoNul(value, `Environment variable ${key}`);
|
|
744
|
-
merged[key] = value;
|
|
745
|
-
}
|
|
746
|
-
return merged;
|
|
747
|
-
}
|
|
748
|
-
function redactValue(value, secrets) {
|
|
749
|
-
let redacted = value;
|
|
750
|
-
for (const secret of [...secrets].sort((left, right) => right.length - left.length)) redacted = redacted.split(secret).join("***");
|
|
751
|
-
return redacted;
|
|
752
|
-
}
|
|
753
|
-
function redactCommand(command, secrets) {
|
|
754
|
-
if (secrets.length === 0) return command;
|
|
755
|
-
return {
|
|
756
|
-
file: redactValue(command.file, secrets),
|
|
757
|
-
args: command.args.map((arg) => redactValue(arg, secrets)),
|
|
758
|
-
cwd: command.cwd === void 0 ? void 0 : redactValue(command.cwd, secrets)
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
function redactError(error, secrets) {
|
|
762
|
-
const source = error;
|
|
763
|
-
const redacted = new Error(redactValue(error.message, secrets));
|
|
764
|
-
if (typeof error.stack === "string") redacted.stack = redactValue(error.stack, secrets);
|
|
765
|
-
if (source.code !== void 0) redacted.code = source.code;
|
|
766
|
-
if (source.errno !== void 0) redacted.errno = source.errno;
|
|
767
|
-
if (typeof source.syscall === "string") redacted.syscall = redactValue(source.syscall, secrets);
|
|
768
|
-
if (typeof source.path === "string") redacted.path = redactValue(source.path, secrets);
|
|
769
|
-
if (Array.isArray(source.spawnargs)) redacted.spawnargs = source.spawnargs.map((arg) => typeof arg === "string" ? redactValue(arg, secrets) : arg);
|
|
770
|
-
return redacted;
|
|
771
|
-
}
|
|
772
|
-
function observedError(plan, error) {
|
|
773
|
-
return plan.redact.length === 0 ? error : redactError(error, plan.redact);
|
|
774
|
-
}
|
|
775
|
-
function parsePlan(file, args, options, context) {
|
|
776
|
-
if (typeof file !== "string" || file === "") throw new TypeError("Exec file must be a non-empty string");
|
|
777
|
-
assertNoNul(file, "Exec file");
|
|
778
|
-
const argv = args === void 0 ? [] : args;
|
|
779
|
-
if (!Array.isArray(argv) || !argv.every((arg) => typeof arg === "string")) throw new TypeError("Exec args must be an array of strings");
|
|
780
|
-
for (const arg of argv) assertNoNul(arg, "Exec args");
|
|
781
|
-
const raw = options === void 0 ? {} : options;
|
|
782
|
-
if (!isRecord(raw)) throw new TypeError("Exec options must be an object");
|
|
783
|
-
for (const key of Object.keys(raw)) if (!OPTION_KEYS.has(key)) throw new TypeError(`Unknown exec option \`${key}\``);
|
|
784
|
-
const { cwd, input, reject, timeout, signal, maxBuffer, forceKillAfterDelay } = raw;
|
|
785
|
-
if (cwd !== void 0) {
|
|
786
|
-
if (typeof cwd !== "string" || cwd === "") throw new TypeError("Exec option `cwd` must be a non-empty string");
|
|
787
|
-
assertNoNul(cwd, "Exec option `cwd`");
|
|
788
|
-
}
|
|
789
|
-
if (input !== void 0 && typeof input !== "string") throw new TypeError("Exec option `input` must be a string");
|
|
790
|
-
if (reject !== void 0 && typeof reject !== "boolean") throw new TypeError("Exec option `reject` must be a boolean");
|
|
791
|
-
if (signal !== void 0 && !(signal instanceof AbortSignal)) throw new TypeError("Exec option `signal` must be an AbortSignal");
|
|
792
|
-
const stdin = raw.stdin === void 0 ? "pipe" : readEnum(raw.stdin, "stdin", ["pipe", "inherit"]);
|
|
793
|
-
const stdout = raw.stdout === void 0 ? "pipe" : readEnum(raw.stdout, "stdout", ["pipe", "inherit"]);
|
|
794
|
-
const stderr = raw.stderr === void 0 ? "pipe" : readEnum(raw.stderr, "stderr", ["pipe", "inherit"]);
|
|
795
|
-
if (input !== void 0 && stdin === "inherit") throw new TypeError("Exec option `input` cannot be combined with stdin inherit");
|
|
796
|
-
const command = {
|
|
797
|
-
file,
|
|
798
|
-
args: [...argv],
|
|
799
|
-
cwd
|
|
800
|
-
};
|
|
801
|
-
const redact = raw.redact === void 0 ? [] : readRedact(raw.redact);
|
|
802
|
-
return {
|
|
803
|
-
command,
|
|
804
|
-
reported: redactCommand(command, redact),
|
|
805
|
-
env: mergeEnvironment(context.env, raw.env),
|
|
806
|
-
input,
|
|
807
|
-
reject: reject ?? true,
|
|
808
|
-
timeout: timeout === void 0 ? void 0 : readInteger(timeout, "Exec option `timeout`", 1, MAX_DELAY),
|
|
809
|
-
signal: signal === void 0 ? context.signal : AbortSignal.any([context.signal, signal]),
|
|
810
|
-
maxBuffer: maxBuffer === void 0 ? 1e8 : readInteger(maxBuffer, "Exec option `maxBuffer`", 1, Number.MAX_SAFE_INTEGER),
|
|
811
|
-
forceKillAfterDelay: forceKillAfterDelay === void 0 ? 5e3 : readInteger(forceKillAfterDelay, "Exec option `forceKillAfterDelay`", 0, MAX_DELAY),
|
|
812
|
-
stdin,
|
|
813
|
-
stdout,
|
|
814
|
-
stderr,
|
|
815
|
-
interactive: raw.interactive === void 0 ? false : readBoolean(raw.interactive, "interactive"),
|
|
816
|
-
display: raw.display === void 0 ? true : readBoolean(raw.display, "display"),
|
|
817
|
-
redact
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
function stripFinalNewline(text) {
|
|
821
|
-
if (text.endsWith("\r\n")) return text.slice(0, -2);
|
|
822
|
-
if (text.endsWith("\n")) return text.slice(0, -1);
|
|
823
|
-
return text;
|
|
824
|
-
}
|
|
825
|
-
function collect(stream, limit, onLimit) {
|
|
826
|
-
const chunks = [];
|
|
827
|
-
let length = 0;
|
|
828
|
-
let exceeded = false;
|
|
829
|
-
stream.on("data", (chunk) => {
|
|
830
|
-
if (exceeded) return;
|
|
831
|
-
const remaining = limit - length;
|
|
832
|
-
if (chunk.length <= remaining) {
|
|
833
|
-
chunks.push(chunk);
|
|
834
|
-
length += chunk.length;
|
|
835
|
-
return;
|
|
494
|
+
//#region src/logger.ts
|
|
495
|
+
function interpolate(message, properties) {
|
|
496
|
+
return message.replace(/\{([^{}]+)\}/g, (placeholder, key) => {
|
|
497
|
+
if (!Object.hasOwn(properties, key)) return placeholder;
|
|
498
|
+
try {
|
|
499
|
+
return String(properties[key]);
|
|
500
|
+
} catch {
|
|
501
|
+
return placeholder;
|
|
836
502
|
}
|
|
837
|
-
chunks.push(chunk.subarray(0, remaining));
|
|
838
|
-
length = limit;
|
|
839
|
-
exceeded = true;
|
|
840
|
-
onLimit();
|
|
841
503
|
});
|
|
842
|
-
return () => stripFinalNewline(Buffer.concat(chunks).toString("utf8"));
|
|
843
|
-
}
|
|
844
|
-
function toResult(command, outcome, stdout, stderr) {
|
|
845
|
-
const failed = {
|
|
846
|
-
status: "failed",
|
|
847
|
-
command,
|
|
848
|
-
stdout,
|
|
849
|
-
stderr
|
|
850
|
-
};
|
|
851
|
-
if (outcome.cause) return {
|
|
852
|
-
...failed,
|
|
853
|
-
reason: outcome.cause
|
|
854
|
-
};
|
|
855
|
-
if (outcome.spawnError) return {
|
|
856
|
-
...failed,
|
|
857
|
-
reason: "spawn",
|
|
858
|
-
error: outcome.spawnError
|
|
859
|
-
};
|
|
860
|
-
const { code, signal } = outcome.exit ?? {
|
|
861
|
-
code: null,
|
|
862
|
-
signal: null
|
|
863
|
-
};
|
|
864
|
-
if (signal !== null) return {
|
|
865
|
-
...failed,
|
|
866
|
-
reason: "signal",
|
|
867
|
-
signal
|
|
868
|
-
};
|
|
869
|
-
if (code !== 0) return {
|
|
870
|
-
...failed,
|
|
871
|
-
reason: "exit-code",
|
|
872
|
-
exitCode: code ?? 1
|
|
873
|
-
};
|
|
874
|
-
return {
|
|
875
|
-
status: "succeeded",
|
|
876
|
-
command,
|
|
877
|
-
stdout,
|
|
878
|
-
stderr,
|
|
879
|
-
exitCode: 0
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
function isSpawnErrno(error) {
|
|
883
|
-
return error instanceof Error && "syscall" in error;
|
|
884
|
-
}
|
|
885
|
-
function commandStdio(plan) {
|
|
886
|
-
return {
|
|
887
|
-
stdin: plan.stdin === "inherit" ? "inherit" : plan.input === void 0 ? "ignore" : "input",
|
|
888
|
-
stdout: plan.stdout,
|
|
889
|
-
stderr: plan.stderr
|
|
890
|
-
};
|
|
891
504
|
}
|
|
892
|
-
function
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
return spawn(plan.command.file, plan.command.args, {
|
|
899
|
-
cwd: plan.command.cwd,
|
|
900
|
-
env: plan.env,
|
|
901
|
-
shell: false,
|
|
902
|
-
windowsHide: true,
|
|
903
|
-
stdio: [
|
|
904
|
-
stdio.stdin === "inherit" ? "inherit" : stdio.stdin === "input" ? "pipe" : "ignore",
|
|
905
|
-
stdio.stdout,
|
|
906
|
-
stdio.stderr
|
|
907
|
-
]
|
|
908
|
-
});
|
|
909
|
-
} catch (error) {
|
|
910
|
-
if (isSpawnErrno(error)) return error;
|
|
911
|
-
throw error;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
function runPlan(plan) {
|
|
915
|
-
const outcome = {
|
|
916
|
-
cause: void 0,
|
|
917
|
-
spawnError: void 0,
|
|
918
|
-
exit: void 0
|
|
919
|
-
};
|
|
920
|
-
if (plan.signal.aborted) {
|
|
921
|
-
outcome.cause = "aborted";
|
|
922
|
-
return Promise.resolve(toResult(plan.reported, outcome, "", ""));
|
|
923
|
-
}
|
|
924
|
-
const child = startChild(plan);
|
|
925
|
-
if (child instanceof Error) {
|
|
926
|
-
outcome.spawnError = observedError(plan, child);
|
|
927
|
-
return Promise.resolve(toResult(plan.reported, outcome, "", ""));
|
|
928
|
-
}
|
|
929
|
-
return new Promise((resolve) => {
|
|
930
|
-
const stdout = child.stdout;
|
|
931
|
-
const stderr = child.stderr;
|
|
932
|
-
let settled = false;
|
|
933
|
-
let killTimer;
|
|
934
|
-
const settle = () => {
|
|
935
|
-
if (settled) return;
|
|
936
|
-
settled = true;
|
|
937
|
-
clearTimeout(timeoutTimer);
|
|
938
|
-
clearTimeout(killTimer);
|
|
939
|
-
plan.signal.removeEventListener("abort", onAbort);
|
|
940
|
-
resolve(toResult(plan.reported, outcome, readStdout(), readStderr()));
|
|
941
|
-
};
|
|
942
|
-
const release = () => {
|
|
943
|
-
stdout?.destroy();
|
|
944
|
-
stderr?.destroy();
|
|
945
|
-
settle();
|
|
505
|
+
function createBoundLogger(printer, category, boundProperties) {
|
|
506
|
+
function emit(level, kind, input, properties = {}) {
|
|
507
|
+
const rawMessage = input instanceof Error ? input.message : input;
|
|
508
|
+
const mergedProperties = {
|
|
509
|
+
...boundProperties,
|
|
510
|
+
...properties
|
|
946
511
|
};
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
512
|
+
if (input instanceof Error) mergedProperties.error = input;
|
|
513
|
+
const summarizedProperties = summarizeErrors(mergedProperties);
|
|
514
|
+
printer.print({
|
|
515
|
+
type: "log",
|
|
516
|
+
record: {
|
|
517
|
+
level,
|
|
518
|
+
kind,
|
|
519
|
+
category,
|
|
520
|
+
message: interpolate(rawMessage, mergedProperties),
|
|
521
|
+
rawMessage,
|
|
522
|
+
properties: summarizedProperties,
|
|
523
|
+
timestamp: Date.now()
|
|
953
524
|
}
|
|
954
|
-
child.kill("SIGTERM");
|
|
955
|
-
killTimer = setTimeout(() => child.kill("SIGKILL"), plan.forceKillAfterDelay);
|
|
956
|
-
};
|
|
957
|
-
const onAbort = () => terminate("aborted");
|
|
958
|
-
const readStdout = stdout ? collect(stdout, plan.maxBuffer, () => terminate("output-limit")) : () => "";
|
|
959
|
-
const readStderr = stderr ? collect(stderr, plan.maxBuffer, () => terminate("output-limit")) : () => "";
|
|
960
|
-
const timeoutTimer = plan.timeout === void 0 ? void 0 : setTimeout(() => terminate("timeout"), plan.timeout);
|
|
961
|
-
plan.signal.addEventListener("abort", onAbort, { once: true });
|
|
962
|
-
child.on("error", (error) => {
|
|
963
|
-
if (child.pid !== void 0) return;
|
|
964
|
-
outcome.spawnError = observedError(plan, error);
|
|
965
|
-
release();
|
|
966
525
|
});
|
|
967
|
-
child.on("exit", (code, signal) => {
|
|
968
|
-
outcome.exit = {
|
|
969
|
-
code,
|
|
970
|
-
signal
|
|
971
|
-
};
|
|
972
|
-
clearTimeout(killTimer);
|
|
973
|
-
if (outcome.cause) release();
|
|
974
|
-
});
|
|
975
|
-
child.on("close", settle);
|
|
976
|
-
if (child.stdin) {
|
|
977
|
-
child.stdin.on("error", () => void 0);
|
|
978
|
-
child.stdin.end(plan.input);
|
|
979
|
-
}
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
function formatArgument(value) {
|
|
983
|
-
return value === "" || /[\s"'\\$`;|&<>*?()[\]{}~#!]/.test(value) ? JSON.stringify(value) : value;
|
|
984
|
-
}
|
|
985
|
-
function formatExecCommand(command) {
|
|
986
|
-
return [command.file, ...command.args].map(formatArgument).join(" ");
|
|
987
|
-
}
|
|
988
|
-
function failureMessage(plan, result) {
|
|
989
|
-
const display = formatExecCommand(plan.reported);
|
|
990
|
-
switch (result.reason) {
|
|
991
|
-
case "exit-code": return `Command failed with exit code ${result.exitCode}: ${display}`;
|
|
992
|
-
case "signal": return `Command was terminated by ${result.signal}: ${display}`;
|
|
993
|
-
case "spawn": return `Command could not be started (${result.error.code ?? result.error.message}): ${display}`;
|
|
994
|
-
case "timeout": return `Command timed out after ${plan.timeout} ms: ${display}`;
|
|
995
|
-
case "aborted": return `Command was aborted: ${display}`;
|
|
996
|
-
case "output-limit": return `Command output exceeded ${plan.maxBuffer} bytes: ${display}`;
|
|
997
|
-
case "json-output":
|
|
998
|
-
case "non-interactive":
|
|
999
|
-
case "overlapping": return `${REFUSAL_MESSAGE[result.reason]}: ${display}`;
|
|
1000
526
|
}
|
|
1001
|
-
}
|
|
1002
|
-
function emptyFailure(plan, reason) {
|
|
1003
527
|
return {
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
528
|
+
category,
|
|
529
|
+
trace: (message, properties) => emit("trace", "message", message, properties),
|
|
530
|
+
debug: (message, properties) => emit("debug", "message", message, properties),
|
|
531
|
+
info: (message, properties) => emit("info", "message", message, properties),
|
|
532
|
+
warn: (message, properties) => emit("warning", "message", message, properties),
|
|
533
|
+
error: (message, properties) => emit("error", "message", message, properties),
|
|
534
|
+
fatal: (message, properties) => emit("fatal", "message", message, properties),
|
|
535
|
+
step: (message, properties) => emit("info", "step", message, properties),
|
|
536
|
+
success: (message, properties) => emit("info", "success", message, properties),
|
|
537
|
+
with: (properties) => createBoundLogger(printer, category, {
|
|
538
|
+
...boundProperties,
|
|
539
|
+
...properties
|
|
540
|
+
}),
|
|
541
|
+
getChild: (child) => createBoundLogger(printer, [...category, child], boundProperties)
|
|
1009
542
|
};
|
|
1010
543
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
return
|
|
1014
|
-
}
|
|
1015
|
-
function blockedReason(plan, context, stdio) {
|
|
1016
|
-
const inherited = stdio.stdin === "inherit" || stdio.stdout === "inherit" || stdio.stderr === "inherit";
|
|
1017
|
-
if (context.mode.format === "json" && inherited) return "json-output";
|
|
1018
|
-
if (plan.interactive && !context.mode.interactive) return "non-interactive";
|
|
1019
|
-
}
|
|
1020
|
-
async function execute(file, args, options, context) {
|
|
1021
|
-
const plan = parsePlan(file, args, options, context);
|
|
1022
|
-
if (plan.signal.aborted) return complete(plan, emptyFailure(plan, "aborted"));
|
|
1023
|
-
const stdio = commandStdio(plan);
|
|
1024
|
-
const blocked = blockedReason(plan, context, stdio);
|
|
1025
|
-
if (blocked) return complete(plan, emptyFailure(plan, blocked));
|
|
1026
|
-
const holdTerminal = usesTerminal(plan, stdio);
|
|
1027
|
-
if (holdTerminal && !context.terminal.acquire("exec")) return complete(plan, emptyFailure(plan, "overlapping"));
|
|
1028
|
-
try {
|
|
1029
|
-
if (plan.display) {
|
|
1030
|
-
const { file, args, cwd } = plan.reported;
|
|
1031
|
-
await context.print({
|
|
1032
|
-
type: "exec",
|
|
1033
|
-
phase: "start",
|
|
1034
|
-
command: cwd === void 0 ? {
|
|
1035
|
-
file,
|
|
1036
|
-
args
|
|
1037
|
-
} : {
|
|
1038
|
-
file,
|
|
1039
|
-
args,
|
|
1040
|
-
cwd
|
|
1041
|
-
},
|
|
1042
|
-
stdio,
|
|
1043
|
-
interactive: plan.interactive
|
|
1044
|
-
});
|
|
1045
|
-
}
|
|
1046
|
-
return complete(plan, await runPlan(plan));
|
|
1047
|
-
} finally {
|
|
1048
|
-
if (holdTerminal) context.terminal.release();
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
function createExec(context) {
|
|
1052
|
-
return ((file, args, options) => execute(file, args, options, context));
|
|
544
|
+
/** Create the logger for one command invocation. */
|
|
545
|
+
function createLogger(printer, category) {
|
|
546
|
+
return createBoundLogger(printer, [...category], {});
|
|
1053
547
|
}
|
|
1054
548
|
//#endregion
|
|
1055
|
-
//#region src/
|
|
1056
|
-
function
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
} catch {
|
|
1060
|
-
return "[Unserializable]";
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
function normalizeProperty(value, key, ancestors) {
|
|
1064
|
-
try {
|
|
1065
|
-
return normalizeJson(Reflect.get(value, key), ancestors);
|
|
1066
|
-
} catch {
|
|
1067
|
-
return "[Unserializable]";
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
function normalizedKeys(value) {
|
|
549
|
+
//#region src/package-version.ts
|
|
550
|
+
async function findPackageVersion(entry) {
|
|
551
|
+
if (!entry) return void 0;
|
|
552
|
+
let directory;
|
|
1071
553
|
try {
|
|
1072
|
-
|
|
554
|
+
directory = dirname(await realpath(entry));
|
|
1073
555
|
} catch {
|
|
1074
556
|
return;
|
|
1075
557
|
}
|
|
1076
|
-
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
} catch {
|
|
1083
|
-
return noToJson;
|
|
1084
|
-
}
|
|
1085
|
-
if (typeof toJSON !== "function") return noToJson;
|
|
1086
|
-
try {
|
|
1087
|
-
const result = Reflect.apply(toJSON, value, []);
|
|
1088
|
-
return result === value ? noToJson : normalizeJson(result, ancestors);
|
|
1089
|
-
} catch {
|
|
1090
|
-
return noToJson;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
function isArrayBufferView(value) {
|
|
1094
|
-
try {
|
|
1095
|
-
return ArrayBuffer.isView(value);
|
|
1096
|
-
} catch {
|
|
1097
|
-
return false;
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
/** Normalize arbitrary event data into values accepted by JSON.stringify. */
|
|
1101
|
-
function normalizeJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1102
|
-
switch (typeof value) {
|
|
1103
|
-
case "string": return stripVTControlCharacters(value);
|
|
1104
|
-
case "boolean": return value;
|
|
1105
|
-
case "number": return Number.isFinite(value) ? value : null;
|
|
1106
|
-
case "bigint": return value.toString();
|
|
1107
|
-
case "undefined": return "[undefined]";
|
|
1108
|
-
case "symbol": return `[symbol:${stripVTControlCharacters(value.description ?? "")}]`;
|
|
1109
|
-
case "function": return `[function:${stripVTControlCharacters(value.name || "anonymous")}]`;
|
|
1110
|
-
}
|
|
1111
|
-
if (value === null) return null;
|
|
1112
|
-
if (ancestors.has(value)) return "[Circular]";
|
|
1113
|
-
const nextAncestors = new Set(ancestors).add(value);
|
|
1114
|
-
if (value instanceof Error) return normalizeJson(summarizeError(value), nextAncestors);
|
|
1115
|
-
const jsonValue = normalizedToJson(value, nextAncestors);
|
|
1116
|
-
if (jsonValue !== noToJson) return jsonValue;
|
|
1117
|
-
if (isArrayBufferView(value)) {
|
|
1118
|
-
if (value instanceof DataView) return Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
|
|
1119
|
-
try {
|
|
1120
|
-
return Array.from(value, (item) => normalizeJson(item, nextAncestors));
|
|
1121
|
-
} catch {
|
|
1122
|
-
return "[Unserializable]";
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
if (value instanceof ArrayBuffer) return Array.from(new Uint8Array(value));
|
|
1126
|
-
if (Array.isArray(value)) return Array.from({ length: value.length }, (_, index) => normalizeProperty(value, index, nextAncestors));
|
|
1127
|
-
if (value instanceof Map) try {
|
|
1128
|
-
return [...value].map(([key, item]) => [normalizeJson(key, nextAncestors), normalizeJson(item, nextAncestors)]);
|
|
1129
|
-
} catch {
|
|
1130
|
-
return "[Unserializable]";
|
|
1131
|
-
}
|
|
1132
|
-
if (value instanceof Set) try {
|
|
1133
|
-
return [...value].map((item) => normalizeJson(item, nextAncestors));
|
|
1134
|
-
} catch {
|
|
1135
|
-
return "[Unserializable]";
|
|
1136
|
-
}
|
|
1137
|
-
const keys = normalizedKeys(value);
|
|
1138
|
-
if (!keys) return safeString(value);
|
|
1139
|
-
const result = {};
|
|
1140
|
-
for (const key of keys) result[stripVTControlCharacters(key)] = normalizeProperty(value, key, nextAncestors);
|
|
1141
|
-
return result;
|
|
1142
|
-
}
|
|
1143
|
-
//#endregion
|
|
1144
|
-
//#region src/printer.ts
|
|
1145
|
-
function fallbackValue(value) {
|
|
1146
|
-
try {
|
|
1147
|
-
return String(value);
|
|
1148
|
-
} catch {
|
|
1149
|
-
return "[Unserializable]";
|
|
558
|
+
for (;;) {
|
|
559
|
+
const version = await readPackageVersion(join(directory, "package.json"));
|
|
560
|
+
if (version !== void 0) return version;
|
|
561
|
+
const parent = dirname(directory);
|
|
562
|
+
if (parent === directory) return void 0;
|
|
563
|
+
directory = parent;
|
|
1150
564
|
}
|
|
1151
565
|
}
|
|
1152
|
-
function
|
|
1153
|
-
|
|
1154
|
-
if (value === "auto" || value === "pretty" || value === "json") return value;
|
|
1155
|
-
throw new TypeError(`Invalid ${source} value ${fallbackValue(value)}; expected auto, pretty, or json`);
|
|
1156
|
-
}
|
|
1157
|
-
function readEnvironment(env, key) {
|
|
566
|
+
async function readPackageVersion(path) {
|
|
567
|
+
let text;
|
|
1158
568
|
try {
|
|
1159
|
-
|
|
569
|
+
text = await readFile(path, "utf8");
|
|
1160
570
|
} catch {
|
|
1161
571
|
return;
|
|
1162
572
|
}
|
|
1163
|
-
}
|
|
1164
|
-
/** Resolve output behavior once, before command routing or argument parsing. */
|
|
1165
|
-
function resolveOutputMode(options = {}) {
|
|
1166
|
-
const env = options.env ?? process.env;
|
|
1167
|
-
const configured = validatePreference(options.output, "output");
|
|
1168
|
-
const requested = validatePreference(readEnvironment(env, "HACKSH_OUTPUT"), "HACKSH_OUTPUT") ?? configured ?? "auto";
|
|
1169
|
-
const ci = Boolean(readEnvironment(env, "CI"));
|
|
1170
|
-
const format = requested === "auto" ? ci ? "json" : "pretty" : requested;
|
|
1171
|
-
const inputIsTTY = options.inputIsTTY ?? process.stdin.isTTY === true;
|
|
1172
|
-
const outputIsTTY = options.outputIsTTY ?? process.stdout.isTTY === true;
|
|
1173
|
-
return {
|
|
1174
|
-
format,
|
|
1175
|
-
interactive: format === "pretty" && !ci && inputIsTTY && outputIsTTY,
|
|
1176
|
-
color: format === "pretty" && outputIsTTY && !readEnvironment(env, "NO_COLOR")
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
function isErrorLevel(level) {
|
|
1180
|
-
return level === "warning" || level === "error" || level === "fatal";
|
|
1181
|
-
}
|
|
1182
|
-
function eventStream(event) {
|
|
1183
|
-
if (event.type === "error" || event.type === "exec") return "stderr";
|
|
1184
|
-
if (event.type === "usage") return event.stream;
|
|
1185
|
-
if (event.type === "log" && isErrorLevel(event.record.level)) return "stderr";
|
|
1186
|
-
return "stdout";
|
|
1187
|
-
}
|
|
1188
|
-
function formatTextEvent(event) {
|
|
1189
|
-
switch (event.type) {
|
|
1190
|
-
case "usage": return event.text;
|
|
1191
|
-
case "version": return event.version;
|
|
1192
|
-
case "error": return event.error.kind === "usage" || event.error.kind === "validation" ? event.error.message : `Error: ${event.error.message}`;
|
|
1193
|
-
case "log": return event.record.message;
|
|
1194
|
-
case "prompt": return event.prompt.message;
|
|
1195
|
-
case "exec": return `$ ${formatExecCommand(event.command)}`;
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
/** Undecorated console compatibility adapter; automatic output uses the selected format printer. */
|
|
1199
|
-
const consolePrinter = { print(event) {
|
|
1200
|
-
const text = formatTextEvent(event);
|
|
1201
|
-
return eventStream(event) === "stdout" ? console.log(text) : console.error(text);
|
|
1202
|
-
} };
|
|
1203
|
-
/** Create an undecorated stream-backed compatibility adapter. */
|
|
1204
|
-
function createStreamPrinter(options) {
|
|
1205
|
-
return { print(event) {
|
|
1206
|
-
(eventStream(event) === "stdout" ? options.stdout : options.stderr).write(`${formatTextEvent(event)}\n`);
|
|
1207
|
-
} };
|
|
1208
|
-
}
|
|
1209
|
-
/** Create a one-semantic-event-per-line JSON printer. */
|
|
1210
|
-
function createJsonPrinter(options = {}) {
|
|
1211
|
-
const stdout = options.stdout ?? process.stdout;
|
|
1212
|
-
const stderr = options.stderr ?? process.stderr;
|
|
1213
|
-
return { print(event) {
|
|
1214
|
-
let serialized;
|
|
1215
|
-
try {
|
|
1216
|
-
serialized = JSON.stringify(normalizeJson(event));
|
|
1217
|
-
} catch {
|
|
1218
|
-
serialized = JSON.stringify({
|
|
1219
|
-
type: event.type,
|
|
1220
|
-
serializationError: "Event data could not be serialized"
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
const line = `${serialized}\n`;
|
|
1224
|
-
(eventStream(event) === "stdout" ? stdout : stderr).write(line);
|
|
1225
|
-
} };
|
|
1226
|
-
}
|
|
1227
|
-
/** Serialize printer calls and retain failures until the invocation drains its output. */
|
|
1228
|
-
function createPrintQueue(target) {
|
|
1229
|
-
let pending;
|
|
1230
|
-
let failure;
|
|
1231
|
-
let failed = false;
|
|
1232
|
-
function recordFailure(error) {
|
|
1233
|
-
if (!failed) failure = error;
|
|
1234
|
-
failed = true;
|
|
1235
|
-
}
|
|
1236
|
-
function track(execution, retainFailure) {
|
|
1237
|
-
const barrier = execution.catch((error) => {
|
|
1238
|
-
if (retainFailure) recordFailure(error);
|
|
1239
|
-
});
|
|
1240
|
-
pending = barrier;
|
|
1241
|
-
barrier.then(() => {
|
|
1242
|
-
if (pending === barrier) pending = void 0;
|
|
1243
|
-
});
|
|
1244
|
-
return retainFailure ? barrier : execution;
|
|
1245
|
-
}
|
|
1246
|
-
function enqueue(event, retainFailure) {
|
|
1247
|
-
if (pending) return track(pending.then(() => target.print(event)), retainFailure);
|
|
1248
|
-
try {
|
|
1249
|
-
const result = target.print(event);
|
|
1250
|
-
if (result) return track(Promise.resolve(result), retainFailure);
|
|
1251
|
-
} catch (error) {
|
|
1252
|
-
if (retainFailure) recordFailure(error);
|
|
1253
|
-
else return Promise.reject(error);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
return {
|
|
1257
|
-
printer: { print(event) {
|
|
1258
|
-
return enqueue(event, true);
|
|
1259
|
-
} },
|
|
1260
|
-
async printChecked(event) {
|
|
1261
|
-
await enqueue(event, false);
|
|
1262
|
-
},
|
|
1263
|
-
async drain() {
|
|
1264
|
-
await pending;
|
|
1265
|
-
if (failed) throw failure;
|
|
1266
|
-
}
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
//#endregion
|
|
1270
|
-
//#region src/pretty.ts
|
|
1271
|
-
const unicodeSymbols = {
|
|
1272
|
-
bar: "│",
|
|
1273
|
-
marks: {
|
|
1274
|
-
trace: "·",
|
|
1275
|
-
debug: "◆",
|
|
1276
|
-
info: "●",
|
|
1277
|
-
warning: "▲",
|
|
1278
|
-
error: "■",
|
|
1279
|
-
fatal: "✖",
|
|
1280
|
-
step: "◇",
|
|
1281
|
-
success: "✔",
|
|
1282
|
-
usage: "◆",
|
|
1283
|
-
version: "●",
|
|
1284
|
-
errorEvent: "■",
|
|
1285
|
-
prompt: "?",
|
|
1286
|
-
exec: "$"
|
|
1287
|
-
}
|
|
1288
|
-
};
|
|
1289
|
-
const asciiSymbols = {
|
|
1290
|
-
bar: "|",
|
|
1291
|
-
marks: {
|
|
1292
|
-
trace: ".",
|
|
1293
|
-
debug: ":",
|
|
1294
|
-
info: "i",
|
|
1295
|
-
warning: "!",
|
|
1296
|
-
error: "x",
|
|
1297
|
-
fatal: "X",
|
|
1298
|
-
step: ">",
|
|
1299
|
-
success: "+",
|
|
1300
|
-
usage: "?",
|
|
1301
|
-
version: "i",
|
|
1302
|
-
errorEvent: "x",
|
|
1303
|
-
prompt: "?",
|
|
1304
|
-
exec: "$"
|
|
1305
|
-
}
|
|
1306
|
-
};
|
|
1307
|
-
const markStyles = {
|
|
1308
|
-
trace: "gray",
|
|
1309
|
-
debug: "magenta",
|
|
1310
|
-
info: "blue",
|
|
1311
|
-
warning: "yellow",
|
|
1312
|
-
error: "red",
|
|
1313
|
-
fatal: [
|
|
1314
|
-
"bgRed",
|
|
1315
|
-
"white",
|
|
1316
|
-
"bold"
|
|
1317
|
-
],
|
|
1318
|
-
step: "cyan",
|
|
1319
|
-
success: "green",
|
|
1320
|
-
usage: "cyan",
|
|
1321
|
-
version: "blue",
|
|
1322
|
-
errorEvent: "red",
|
|
1323
|
-
prompt: "cyan",
|
|
1324
|
-
exec: "gray"
|
|
1325
|
-
};
|
|
1326
|
-
function safelyInspect(value) {
|
|
1327
|
-
if (typeof value === "string") return value;
|
|
1328
573
|
try {
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
colors: false,
|
|
1332
|
-
compact: false,
|
|
1333
|
-
depth: null,
|
|
1334
|
-
sorted: true
|
|
1335
|
-
});
|
|
574
|
+
const { version } = JSON.parse(text);
|
|
575
|
+
return typeof version === "string" ? version : void 0;
|
|
1336
576
|
} catch {
|
|
1337
|
-
return
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
function propertyLines(properties) {
|
|
1341
|
-
const lines = [];
|
|
1342
|
-
for (const key of Object.keys(properties).sort()) {
|
|
1343
|
-
const rendered = ` ${key}: ${safelyInspect(properties[key])}`.split("\n");
|
|
1344
|
-
lines.push(rendered[0] ?? "");
|
|
1345
|
-
for (const continuation of rendered.slice(1)) lines.push(` ${continuation}`);
|
|
1346
|
-
}
|
|
1347
|
-
return lines;
|
|
1348
|
-
}
|
|
1349
|
-
function logLines(record) {
|
|
1350
|
-
const category = record.category.length > 0 ? `[${record.category.join(":")}] ` : "";
|
|
1351
|
-
const message = record.message.split("\n");
|
|
1352
|
-
return [
|
|
1353
|
-
`${category}${message[0] ?? ""}`,
|
|
1354
|
-
...message.slice(1),
|
|
1355
|
-
...propertyLines(record.properties)
|
|
1356
|
-
];
|
|
1357
|
-
}
|
|
1358
|
-
function errorLines(error) {
|
|
1359
|
-
const lines = error.message.split("\n");
|
|
1360
|
-
const details = {};
|
|
1361
|
-
if (error.cause !== void 0) details.cause = error.cause;
|
|
1362
|
-
if (error.properties !== void 0) Object.assign(details, error.properties);
|
|
1363
|
-
if (error.value !== void 0) details.value = error.value;
|
|
1364
|
-
return [...lines, ...propertyLines(details)];
|
|
1365
|
-
}
|
|
1366
|
-
function eventMark(event) {
|
|
1367
|
-
if (event.type === "usage") return "usage";
|
|
1368
|
-
if (event.type === "version") return "version";
|
|
1369
|
-
if (event.type === "error") return "errorEvent";
|
|
1370
|
-
if (event.type === "prompt") return event.prompt.status === "submitted" ? "success" : "prompt";
|
|
1371
|
-
if (event.type === "exec") return "exec";
|
|
1372
|
-
return event.record.kind === "message" ? event.record.level : event.record.kind;
|
|
1373
|
-
}
|
|
1374
|
-
function eventLines(event) {
|
|
1375
|
-
switch (event.type) {
|
|
1376
|
-
case "usage": return event.text.split("\n");
|
|
1377
|
-
case "version": return event.version.split("\n");
|
|
1378
|
-
case "error": return errorLines(event.error);
|
|
1379
|
-
case "log": return logLines(event.record);
|
|
1380
|
-
case "prompt": {
|
|
1381
|
-
const { prompt } = event;
|
|
1382
|
-
if (prompt.status === "submitted") {
|
|
1383
|
-
const result = prompt.kind === "select" ? prompt.choices.find(({ value }) => value === prompt.value)?.label ?? prompt.value : prompt.kind === "confirm" ? prompt.value ? "Yes" : "No" : prompt.value;
|
|
1384
|
-
return [`${prompt.message}: ${result}`];
|
|
1385
|
-
}
|
|
1386
|
-
const lines = [prompt.message];
|
|
1387
|
-
if (prompt.kind === "select") for (const choice of prompt.choices) lines.push(`${choice.selected ? ">" : " "} ${choice.label}${choice.hint ? ` — ${choice.hint}` : ""}`);
|
|
1388
|
-
else if (prompt.kind === "confirm") lines.push(`${prompt.value ? "Yes" : "No"}${prompt.defaultValue === void 0 ? "" : ` (default: ${prompt.defaultValue ? "yes" : "no"})`}`);
|
|
1389
|
-
else lines.push(prompt.value || (prompt.placeholder ? `(${prompt.placeholder})` : ""));
|
|
1390
|
-
if (prompt.error) lines.push(prompt.error);
|
|
1391
|
-
return lines;
|
|
1392
|
-
}
|
|
1393
|
-
case "exec": {
|
|
1394
|
-
const lines = [formatExecCommand(event.command)];
|
|
1395
|
-
if (event.command.cwd !== void 0) lines.push(...propertyLines({ cwd: event.command.cwd }));
|
|
1396
|
-
return lines;
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
function colorize(enabled, style, text) {
|
|
1401
|
-
return enabled ? styleText(style, text, { validateStream: false }) : text;
|
|
1402
|
-
}
|
|
1403
|
-
const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1404
|
-
const combiningMark = /\p{Mark}/u;
|
|
1405
|
-
const pictograph = /\p{Extended_Pictographic}/u;
|
|
1406
|
-
function isWideCodePoint(codePoint) {
|
|
1407
|
-
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 131072 && codePoint <= 262141);
|
|
1408
|
-
}
|
|
1409
|
-
function displayWidth(text) {
|
|
1410
|
-
let width = 0;
|
|
1411
|
-
for (const { segment } of graphemeSegmenter.segment(stripVTControlCharacters(text))) {
|
|
1412
|
-
if (segment === " ") {
|
|
1413
|
-
width += 8 - width % 8;
|
|
1414
|
-
continue;
|
|
1415
|
-
}
|
|
1416
|
-
if (pictograph.test(segment) || Array.from(segment).some((value) => isWideCodePoint(value.codePointAt(0)))) {
|
|
1417
|
-
width += 2;
|
|
1418
|
-
continue;
|
|
1419
|
-
}
|
|
1420
|
-
for (const value of segment) {
|
|
1421
|
-
const codePoint = value.codePointAt(0);
|
|
1422
|
-
if (codePoint < 32 || codePoint >= 127 && codePoint < 160) continue;
|
|
1423
|
-
if (codePoint === 8205 || codePoint >= 65024 && codePoint <= 65039) continue;
|
|
1424
|
-
if (!combiningMark.test(value)) width += 1;
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
return width;
|
|
1428
|
-
}
|
|
1429
|
-
function renderedRows(rendered, columns) {
|
|
1430
|
-
const width = Number.isInteger(columns) && columns !== void 0 && columns > 0 ? columns : void 0;
|
|
1431
|
-
return rendered.split("\n").reduce((rows, line) => {
|
|
1432
|
-
if (!width) return rows + 1;
|
|
1433
|
-
return rows + Math.max(1, Math.ceil(displayWidth(line) / width));
|
|
1434
|
-
}, 0);
|
|
1435
|
-
}
|
|
1436
|
-
/** Create the fixed human-readable renderer used for pretty output. */
|
|
1437
|
-
function createPrettyPrinter(options) {
|
|
1438
|
-
const stdout = options.stdout ?? process.stdout;
|
|
1439
|
-
const stderr = options.stderr ?? process.stderr;
|
|
1440
|
-
const symbols = options.unicode ? unicodeSymbols : asciiSymbols;
|
|
1441
|
-
let activePrompt;
|
|
1442
|
-
function render(event) {
|
|
1443
|
-
const mark = eventMark(event);
|
|
1444
|
-
const lines = eventLines(event);
|
|
1445
|
-
const firstPrefix = colorize(options.color, markStyles[mark], symbols.marks[mark]);
|
|
1446
|
-
const barPrefix = colorize(options.color, "gray", symbols.bar);
|
|
1447
|
-
return lines.map((line, index) => `${index === 0 ? firstPrefix : barPrefix}${line ? ` ${line}` : ""}`).join("\n");
|
|
1448
|
-
}
|
|
1449
|
-
function eraseActivePrompt() {
|
|
1450
|
-
return activePrompt ? "\x1B[1A\x1B[2K".repeat(activePrompt.lines) : "";
|
|
577
|
+
return;
|
|
1451
578
|
}
|
|
1452
|
-
return { print(event) {
|
|
1453
|
-
const rendered = render(event);
|
|
1454
|
-
if (event.type === "prompt") {
|
|
1455
|
-
const erase = eraseActivePrompt();
|
|
1456
|
-
if (event.prompt.status === "closed") {
|
|
1457
|
-
activePrompt = void 0;
|
|
1458
|
-
if (erase) stdout.write(erase);
|
|
1459
|
-
return;
|
|
1460
|
-
}
|
|
1461
|
-
if (event.prompt.status === "submitted") activePrompt = void 0;
|
|
1462
|
-
else activePrompt = {
|
|
1463
|
-
rendered,
|
|
1464
|
-
lines: renderedRows(rendered, stdout.columns)
|
|
1465
|
-
};
|
|
1466
|
-
stdout.write(`${erase}${rendered}\n`);
|
|
1467
|
-
return;
|
|
1468
|
-
}
|
|
1469
|
-
const erase = eraseActivePrompt();
|
|
1470
|
-
if (!activePrompt) {
|
|
1471
|
-
(eventStream(event) === "stdout" ? stdout : stderr).write(`${rendered}\n`);
|
|
1472
|
-
return;
|
|
1473
|
-
}
|
|
1474
|
-
stdout.write(erase);
|
|
1475
|
-
if (event.type === "error") activePrompt = void 0;
|
|
1476
|
-
(eventStream(event) === "stdout" ? stdout : stderr).write(`${rendered}\n`);
|
|
1477
|
-
if (activePrompt) stdout.write(`${activePrompt.rendered}\n`);
|
|
1478
|
-
} };
|
|
1479
579
|
}
|
|
1480
580
|
//#endregion
|
|
1481
581
|
//#region src/prompts.ts
|
|
@@ -1761,103 +861,6 @@ var PromptController = class {
|
|
|
1761
861
|
}
|
|
1762
862
|
};
|
|
1763
863
|
//#endregion
|
|
1764
|
-
//#region src/logger.ts
|
|
1765
|
-
function interpolate(message, properties) {
|
|
1766
|
-
return message.replace(/\{([^{}]+)\}/g, (placeholder, key) => {
|
|
1767
|
-
if (!Object.hasOwn(properties, key)) return placeholder;
|
|
1768
|
-
try {
|
|
1769
|
-
return String(properties[key]);
|
|
1770
|
-
} catch {
|
|
1771
|
-
return placeholder;
|
|
1772
|
-
}
|
|
1773
|
-
});
|
|
1774
|
-
}
|
|
1775
|
-
function createBoundLogger(printer, category, boundProperties, prompts) {
|
|
1776
|
-
function emit(level, kind, input, properties = {}) {
|
|
1777
|
-
const rawMessage = input instanceof Error ? input.message : input;
|
|
1778
|
-
const mergedProperties = {
|
|
1779
|
-
...boundProperties,
|
|
1780
|
-
...properties
|
|
1781
|
-
};
|
|
1782
|
-
if (input instanceof Error) mergedProperties.error = input;
|
|
1783
|
-
const summarizedProperties = summarizeErrors(mergedProperties);
|
|
1784
|
-
printer.print({
|
|
1785
|
-
type: "log",
|
|
1786
|
-
record: {
|
|
1787
|
-
level,
|
|
1788
|
-
kind,
|
|
1789
|
-
category,
|
|
1790
|
-
message: interpolate(rawMessage, mergedProperties),
|
|
1791
|
-
rawMessage,
|
|
1792
|
-
properties: summarizedProperties,
|
|
1793
|
-
timestamp: Date.now()
|
|
1794
|
-
}
|
|
1795
|
-
});
|
|
1796
|
-
}
|
|
1797
|
-
return {
|
|
1798
|
-
category,
|
|
1799
|
-
trace: (message, properties) => emit("trace", "message", message, properties),
|
|
1800
|
-
debug: (message, properties) => emit("debug", "message", message, properties),
|
|
1801
|
-
info: (message, properties) => emit("info", "message", message, properties),
|
|
1802
|
-
warn: (message, properties) => emit("warning", "message", message, properties),
|
|
1803
|
-
error: (message, properties) => emit("error", "message", message, properties),
|
|
1804
|
-
fatal: (message, properties) => emit("fatal", "message", message, properties),
|
|
1805
|
-
step: (message, properties) => emit("info", "step", message, properties),
|
|
1806
|
-
success: (message, properties) => emit("info", "success", message, properties),
|
|
1807
|
-
select: (options) => prompts.select(options),
|
|
1808
|
-
confirm: (options) => prompts.confirm(options),
|
|
1809
|
-
text: (options) => prompts.text(options),
|
|
1810
|
-
with: (properties) => createBoundLogger(printer, category, {
|
|
1811
|
-
...boundProperties,
|
|
1812
|
-
...properties
|
|
1813
|
-
}, prompts),
|
|
1814
|
-
getChild: (child) => createBoundLogger(printer, [...category, child], boundProperties, prompts)
|
|
1815
|
-
};
|
|
1816
|
-
}
|
|
1817
|
-
/** Create the logger for one command invocation. */
|
|
1818
|
-
function createLogger(printer, category, options) {
|
|
1819
|
-
const prompts = new PromptController({
|
|
1820
|
-
interactive: options.interactive,
|
|
1821
|
-
input: options.input,
|
|
1822
|
-
print: (event) => options.printPrompt(event),
|
|
1823
|
-
terminal: options.terminal,
|
|
1824
|
-
signal: options.signal
|
|
1825
|
-
});
|
|
1826
|
-
return createBoundLogger(printer, [...category], {}, prompts);
|
|
1827
|
-
}
|
|
1828
|
-
//#endregion
|
|
1829
|
-
//#region src/package-version.ts
|
|
1830
|
-
async function findPackageVersion(entry) {
|
|
1831
|
-
if (!entry) return void 0;
|
|
1832
|
-
let directory;
|
|
1833
|
-
try {
|
|
1834
|
-
directory = dirname(await realpath(entry));
|
|
1835
|
-
} catch {
|
|
1836
|
-
return;
|
|
1837
|
-
}
|
|
1838
|
-
for (;;) {
|
|
1839
|
-
const version = await readPackageVersion(join(directory, "package.json"));
|
|
1840
|
-
if (version !== void 0) return version;
|
|
1841
|
-
const parent = dirname(directory);
|
|
1842
|
-
if (parent === directory) return void 0;
|
|
1843
|
-
directory = parent;
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
async function readPackageVersion(path) {
|
|
1847
|
-
let text;
|
|
1848
|
-
try {
|
|
1849
|
-
text = await readFile(path, "utf8");
|
|
1850
|
-
} catch {
|
|
1851
|
-
return;
|
|
1852
|
-
}
|
|
1853
|
-
try {
|
|
1854
|
-
const { version } = JSON.parse(text);
|
|
1855
|
-
return typeof version === "string" ? version : void 0;
|
|
1856
|
-
} catch {
|
|
1857
|
-
return;
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
//#endregion
|
|
1861
864
|
//#region src/output.ts
|
|
1862
865
|
function consoleWriter(method, stream) {
|
|
1863
866
|
return {
|
|
@@ -1952,12 +955,11 @@ function summarizeUsage(command) {
|
|
|
1952
955
|
});
|
|
1953
956
|
}
|
|
1954
957
|
const envNames = optionEnvNames(command, commandEnvPath(command));
|
|
1955
|
-
const options = Object.entries(shape).filter(([key]) => key !==
|
|
958
|
+
const options = Object.entries(shape).filter(([key]) => key !== POSITIONALS_KEY).map(([key, schema]) => {
|
|
1956
959
|
const info = readArgInfo(schema);
|
|
1957
|
-
const
|
|
1958
|
-
const aliases = argOptions?.alias ? aliasList(argOptions.alias).map((item) => `-${item}`) : [];
|
|
960
|
+
const aliases = info.alias === void 0 ? [] : aliasList(info.alias).map((item) => `-${item}`);
|
|
1959
961
|
const name = kebabCase(key);
|
|
1960
|
-
const valueName =
|
|
962
|
+
const valueName = info.valueName ?? name;
|
|
1961
963
|
const envName = envNames.get(key);
|
|
1962
964
|
return {
|
|
1963
965
|
name,
|
|
@@ -2016,15 +1018,14 @@ function formatUsage(summary) {
|
|
|
2016
1018
|
return lines.join("\n");
|
|
2017
1019
|
}
|
|
2018
1020
|
/** Generate usage text without writing to stdout or stderr. */
|
|
2019
|
-
|
|
1021
|
+
function renderUsage(command) {
|
|
2020
1022
|
return formatUsage(summarizeUsage(command));
|
|
2021
1023
|
}
|
|
2022
|
-
/** Generate
|
|
2023
|
-
async function showUsage(command, options
|
|
1024
|
+
/** Generate a usage event and await its printer. */
|
|
1025
|
+
async function showUsage(command, options) {
|
|
2024
1026
|
const usage = summarizeUsage(command);
|
|
2025
1027
|
const text = formatUsage(usage);
|
|
2026
|
-
|
|
2027
|
-
return (options.printer ?? selectOutputPrinter(options)).print({
|
|
1028
|
+
return options.printer.print({
|
|
2028
1029
|
type: "usage",
|
|
2029
1030
|
usage,
|
|
2030
1031
|
text,
|
|
@@ -2079,8 +1080,7 @@ function resolveCommand(root, rawArgs) {
|
|
|
2079
1080
|
remaining = remaining.slice(index + 1);
|
|
2080
1081
|
}
|
|
2081
1082
|
}
|
|
2082
|
-
async function executeCommand(root, argv,
|
|
2083
|
-
if (root.args && "version" in schemaShape(root.args)) throw new TypeError("Root option `version` conflicts with the builtin --version");
|
|
1083
|
+
async function executeCommand(root, argv, output, env, signal, fallbackVersion = async () => void 0) {
|
|
2084
1084
|
const { printer } = output;
|
|
2085
1085
|
const argEnv = env ?? process.env;
|
|
2086
1086
|
const resolved = resolveCommand(root, argv);
|
|
@@ -2093,7 +1093,7 @@ async function executeCommand(root, argv, displayUsage, output, env, signal, fal
|
|
|
2093
1093
|
return;
|
|
2094
1094
|
}
|
|
2095
1095
|
if (resolved.segments.some(({ tokenized }) => tokenized.help)) {
|
|
2096
|
-
await
|
|
1096
|
+
await showUsage(resolved.command, { printer });
|
|
2097
1097
|
return;
|
|
2098
1098
|
}
|
|
2099
1099
|
for (const parent of resolved.parents) await parseCommandArgs(parent.command, parent.rawArgs, parent.tokenized, argEnv, commandEnvPath(parent.command));
|
|
@@ -2102,13 +1102,18 @@ async function executeCommand(root, argv, displayUsage, output, env, signal, fal
|
|
|
2102
1102
|
signal.throwIfAborted();
|
|
2103
1103
|
const category = resolved.segments.flatMap(({ command }) => command.name === void 0 ? [] : [command.name]);
|
|
2104
1104
|
return resolved.command.run({
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
1105
|
+
command: resolved.command,
|
|
1106
|
+
args: parsed.args,
|
|
1107
|
+
input: {
|
|
1108
|
+
argv: resolved.rawArgs,
|
|
1109
|
+
unknown: parsed.extraArgs,
|
|
1110
|
+
passthrough: parsed.passthrough
|
|
1111
|
+
},
|
|
1112
|
+
logger: createLogger(printer, category),
|
|
1113
|
+
prompt: new PromptController({
|
|
2109
1114
|
interactive: output.interactive,
|
|
2110
1115
|
input: output.input,
|
|
2111
|
-
|
|
1116
|
+
print: (event) => output.printChecked(event),
|
|
2112
1117
|
terminal: output.terminal,
|
|
2113
1118
|
signal
|
|
2114
1119
|
}),
|
|
@@ -2125,14 +1130,6 @@ async function executeCommand(root, argv, displayUsage, output, env, signal, fal
|
|
|
2125
1130
|
signal
|
|
2126
1131
|
});
|
|
2127
1132
|
}
|
|
2128
|
-
function printUsage(displayUsage, command, printer, stream) {
|
|
2129
|
-
return displayUsage(command, { write: (text) => printer.print({
|
|
2130
|
-
type: "usage",
|
|
2131
|
-
usage: summarizeUsage(command),
|
|
2132
|
-
text,
|
|
2133
|
-
stream
|
|
2134
|
-
}) });
|
|
2135
|
-
}
|
|
2136
1133
|
function prepareOutput(options, env) {
|
|
2137
1134
|
const input = options.stdin ?? process.stdin;
|
|
2138
1135
|
const mode = resolveOutputMode({
|
|
@@ -2152,7 +1149,7 @@ function prepareOutput(options, env) {
|
|
|
2152
1149
|
async function runCommand(root, argv, options = {}) {
|
|
2153
1150
|
const output = prepareOutput(options, options.env ?? process.env);
|
|
2154
1151
|
try {
|
|
2155
|
-
return await executeCommand(root, argv,
|
|
1152
|
+
return await executeCommand(root, argv, output, options.env, options.signal ?? new AbortController().signal);
|
|
2156
1153
|
} finally {
|
|
2157
1154
|
await output.drain();
|
|
2158
1155
|
}
|
|
@@ -2207,16 +1204,18 @@ async function reportOutputSetupError(error, options) {
|
|
|
2207
1204
|
});
|
|
2208
1205
|
return drainAndExit(output);
|
|
2209
1206
|
}
|
|
2210
|
-
async function reportCommandError(error, output
|
|
1207
|
+
async function reportCommandError(error, output) {
|
|
2211
1208
|
if (error instanceof HackshExitError && error.message === "") return;
|
|
2212
1209
|
await output.printer.print({
|
|
2213
1210
|
type: "error",
|
|
2214
1211
|
error: summarizeError(error)
|
|
2215
1212
|
});
|
|
2216
|
-
if (error instanceof HackshUsageError) await
|
|
1213
|
+
if (error instanceof HackshUsageError) await showUsage(error.command, {
|
|
1214
|
+
printer: output.printer,
|
|
1215
|
+
stream: "stderr"
|
|
1216
|
+
});
|
|
2217
1217
|
}
|
|
2218
1218
|
async function runMain(command, options = {}) {
|
|
2219
|
-
const displayUsage = options.showUsage ?? showUsage;
|
|
2220
1219
|
let output;
|
|
2221
1220
|
try {
|
|
2222
1221
|
output = prepareOutput(options, options.env ?? process.env);
|
|
@@ -2229,14 +1228,14 @@ async function runMain(command, options = {}) {
|
|
|
2229
1228
|
return drainAndExit(output, signals.exitCode() ?? code);
|
|
2230
1229
|
};
|
|
2231
1230
|
try {
|
|
2232
|
-
await executeCommand(command, options.argv ?? process.argv.slice(2),
|
|
1231
|
+
await executeCommand(command, options.argv ?? process.argv.slice(2), output, options.env, signals.signal, () => findPackageVersion(process.argv[1]));
|
|
2233
1232
|
} catch (error) {
|
|
2234
1233
|
if (options.onError) {
|
|
2235
1234
|
await options.onError(error);
|
|
2236
1235
|
return exit(commandErrorExitCode(error));
|
|
2237
1236
|
}
|
|
2238
1237
|
try {
|
|
2239
|
-
await reportCommandError(error, output
|
|
1238
|
+
await reportCommandError(error, output);
|
|
2240
1239
|
} finally {
|
|
2241
1240
|
await exit(commandErrorExitCode(error));
|
|
2242
1241
|
}
|
|
@@ -2247,13 +1246,7 @@ async function runMain(command, options = {}) {
|
|
|
2247
1246
|
if (signalExitCode !== void 0) return drainAndExit(output, signalExitCode);
|
|
2248
1247
|
await output.drain();
|
|
2249
1248
|
}
|
|
2250
|
-
function createMain(command, options = {}) {
|
|
2251
|
-
return (argv) => runMain(command, argv === void 0 ? options : {
|
|
2252
|
-
...options,
|
|
2253
|
-
argv
|
|
2254
|
-
});
|
|
2255
|
-
}
|
|
2256
1249
|
//#endregion
|
|
2257
|
-
export { HackshExecError, HackshExitError, HackshPromptError, HackshUsageError, HackshValidationError,
|
|
1250
|
+
export { HackshExecError, HackshExitError, HackshPromptError, HackshUsageError, HackshValidationError, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage, summarizeUsage };
|
|
2258
1251
|
|
|
2259
1252
|
//# sourceMappingURL=index.js.map
|