hacksh 0.16.0 → 0.18.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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
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-DJ2CE6TU.js";
1
+ import { a as createExec, c as HackshExitError, d as HackshValidationError, f as summarizeError, i as resolveOutputMode, l as HackshPromptError, n as createJsonPrinter, o as HackshError, p as summarizeErrors, r as createPrintQueue, s as HackshExecError, t as createPrettyPrinter, u as HackshUsageError } from "./pretty-BjzGnVLM.js";
2
2
  import { globalRegistry, safeParseAsync } from "zod/v4/core";
3
3
  import { parseArgs as parseArgs$1 } from "node:util";
4
4
  import { readFile, realpath } from "node:fs/promises";
@@ -619,6 +619,44 @@ function promptError(reason, prompt, message, cause) {
619
619
  function validateMessage(kind, message, choices) {
620
620
  if (typeof message !== "string" || message.length === 0) throw promptError("invalid-options", details$1(kind, message, choices), `Invalid ${kind} prompt: message must be a non-empty string`);
621
621
  }
622
+ function validateChoices(kind, options) {
623
+ const choices = Array.isArray(options?.options) ? options.options.map((option) => typeof option === "object" && option !== null && typeof option.value === "string" ? option.value : "[Invalid choice]") : [];
624
+ const prompt = details$1(kind, options?.message, choices);
625
+ validateMessage(kind, options?.message, choices);
626
+ if (!Array.isArray(options.options) || options.options.length === 0) throw promptError("invalid-options", prompt, `A ${kind} prompt requires at least one choice`);
627
+ const seen = /* @__PURE__ */ new Set();
628
+ for (const option of options.options) {
629
+ if (typeof option !== "object" || option === null || typeof option.value !== "string" || option.value.length === 0 || option.label !== void 0 && typeof option.label !== "string" || option.hint !== void 0 && typeof option.hint !== "string") throw promptError("invalid-options", prompt, `${kind === "select" ? "Select" : "Autocomplete"} choices must have valid string values`);
630
+ if (seen.has(option.value)) throw promptError("invalid-options", prompt, `Duplicate ${kind} choice: ${option.value}`);
631
+ seen.add(option.value);
632
+ }
633
+ return prompt;
634
+ }
635
+ function sortedChoices(options, prompt) {
636
+ const { sortBy } = options;
637
+ if (sortBy === void 0) return options.options;
638
+ if (typeof sortBy !== "function") throw promptError("invalid-options", prompt, "sortBy must be a function");
639
+ let keyType;
640
+ const keyed = options.options.map((item) => {
641
+ let key;
642
+ try {
643
+ key = sortBy(item);
644
+ } catch (cause) {
645
+ throw promptError("input-error", prompt, "Choice sort key could not be computed", cause);
646
+ }
647
+ if (typeof key !== "string" && (typeof key !== "number" || Number.isNaN(key))) throw promptError("invalid-options", prompt, "sortBy must return a string or a non-NaN number");
648
+ const type = typeof key;
649
+ if (keyType !== void 0 && keyType !== type) throw promptError("invalid-options", prompt, "sortBy keys must all have the same type");
650
+ keyType = type;
651
+ return {
652
+ item,
653
+ key
654
+ };
655
+ });
656
+ keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
657
+ return keyed.map(({ item }) => item);
658
+ }
659
+ const graphemeSegmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
622
660
  function asInput(value, prompt) {
623
661
  if (typeof value !== "object" || value === null || typeof Reflect.get(value, "on") !== "function" || typeof Reflect.get(value, "removeListener") !== "function" || typeof Reflect.get(value, "resume") !== "function" || typeof Reflect.get(value, "pause") !== "function") throw promptError("input-error", prompt, "Interactive input is not a readable stream");
624
662
  return value;
@@ -647,7 +685,7 @@ var PromptController = class {
647
685
  throw promptError("printer-error", prompt, "The prompt could not be displayed", cause);
648
686
  }
649
687
  }
650
- async #run(prompt, initial, accept, update, submitted, closed) {
688
+ async #run(prompt, initial, accept, update, submitted, closed, cancelOnEscape = () => true) {
651
689
  if (!this.#interactive) throw promptError("non-interactive", prompt, `Cannot request ${prompt.kind} input in non-interactive mode: ${prompt.message}`);
652
690
  if (this.#signal.aborted) throw promptError("aborted", prompt, "Prompt aborted", this.#signal.reason);
653
691
  const input = asInput(this.#inputValue, prompt);
@@ -689,7 +727,7 @@ var PromptController = class {
689
727
  };
690
728
  const handleKeypress = async (character, key = {}) => {
691
729
  if (settled) return;
692
- if (key.ctrl && key.name === "c" || key.name === "escape") {
730
+ if (key.ctrl && key.name === "c" || key.name === "escape" && cancelOnEscape()) {
693
731
  fail("cancelled", "Prompt cancelled");
694
732
  return;
695
733
  }
@@ -797,36 +835,71 @@ var PromptController = class {
797
835
  }
798
836
  }
799
837
  async select(options) {
800
- const choices = Array.isArray(options?.options) ? options.options.map((option) => typeof option === "object" && option !== null && typeof option.value === "string" ? option.value : "[Invalid choice]") : [];
801
- const prompt = details$1("select", options?.message, choices);
802
- validateMessage("select", options?.message, choices);
803
- if (!Array.isArray(options.options) || options.options.length === 0) throw promptError("invalid-options", prompt, "A select prompt requires at least one choice");
804
- const seen = /* @__PURE__ */ new Set();
805
- for (const option of options.options) {
806
- if (typeof option !== "object" || option === null || typeof option.value !== "string" || option.value.length === 0 || option.label !== void 0 && typeof option.label !== "string" || option.hint !== void 0 && typeof option.hint !== "string") throw promptError("invalid-options", prompt, "Select choices must have valid string values");
807
- if (seen.has(option.value)) throw promptError("invalid-options", prompt, `Duplicate select choice: ${option.value}`);
808
- seen.add(option.value);
809
- }
810
- let selected = options.initialValue === void 0 ? 0 : choices.indexOf(options.initialValue);
838
+ const prompt = validateChoices("select", options);
839
+ const choices = sortedChoices(options, prompt);
840
+ let selected = options.initialValue === void 0 ? 0 : choices.findIndex(({ value }) => value === options.initialValue);
811
841
  if (selected < 0) throw promptError("invalid-options", prompt, `Unknown initial select value: ${String(options.initialValue)}`);
812
842
  const state = (status = "active") => ({
813
843
  kind: "select",
814
844
  message: options.message,
815
845
  status,
816
- value: options.options[selected].value,
817
- choices: options.options.map(({ value, label, hint }, index) => ({
846
+ value: choices[selected].value,
847
+ choices: choices.map(({ value, label, hint }, index) => ({
818
848
  value,
819
849
  label: label ?? value,
820
850
  ...hint === void 0 ? {} : { hint },
821
851
  selected: index === selected
822
852
  }))
823
853
  });
824
- return await this.#run(prompt, state(), (_character, key) => key.name === "return" || key.name === "enter" ? options.options[selected].value : void 0, (_character, key) => {
854
+ return await this.#run(prompt, state(), (_character, key) => key.name === "return" || key.name === "enter" ? choices[selected].value : void 0, (_character, key) => {
825
855
  if (key.name !== "up" && key.name !== "down") return void 0;
826
- selected = (selected + (key.name === "up" ? -1 : 1) + options.options.length) % options.options.length;
856
+ selected = (selected + (key.name === "up" ? -1 : 1) + choices.length) % choices.length;
827
857
  return state();
828
858
  }, () => state("submitted"), () => state("closed"));
829
859
  }
860
+ async autocomplete(options) {
861
+ const prompt = validateChoices("autocomplete", options);
862
+ if (options.placeholder !== void 0 && typeof options.placeholder !== "string") throw promptError("invalid-options", prompt, "Autocomplete placeholder must be a string");
863
+ const choices = sortedChoices(options, prompt);
864
+ let query = "";
865
+ let selected = -1;
866
+ let filtered = choices;
867
+ const state = (status = "active") => ({
868
+ kind: "autocomplete",
869
+ message: options.message,
870
+ status,
871
+ query,
872
+ phase: selected < 0 ? "input" : "select",
873
+ ...options.placeholder === void 0 ? {} : { placeholder: options.placeholder },
874
+ ...selected < 0 ? {} : { value: filtered[selected].value },
875
+ choices: filtered.map(({ value, label, hint }, index) => ({
876
+ value,
877
+ label: label ?? value,
878
+ ...hint === void 0 ? {} : { hint },
879
+ selected: index === selected
880
+ }))
881
+ });
882
+ return await this.#run(prompt, state(), (_character, key) => selected >= 0 && (key.name === "return" || key.name === "enter") ? filtered[selected].value : void 0, (character, key) => {
883
+ if (key.name === "down") {
884
+ if (selected >= filtered.length - 1) return void 0;
885
+ selected += 1;
886
+ } else if (key.name === "up") {
887
+ if (selected < 0) return void 0;
888
+ selected -= 1;
889
+ } else if (key.name === "escape") selected = -1;
890
+ else {
891
+ if (key.name === "backspace") {
892
+ const last = graphemeSegmenter.segment(query).containing(query.length - 1);
893
+ query = last === void 0 ? "" : query.slice(0, last.index);
894
+ } else if (!key.ctrl && !key.meta && character.length > 0 && !/\p{Cc}/u.test(character)) query += character;
895
+ else return void 0;
896
+ selected = -1;
897
+ const search = query.toLowerCase();
898
+ filtered = search.length === 0 ? choices : choices.filter(({ value, label }) => (label ?? value).toLowerCase().includes(search));
899
+ }
900
+ return state();
901
+ }, () => state("submitted"), () => state("closed"), () => selected < 0);
902
+ }
830
903
  async confirm(options) {
831
904
  const prompt = details$1("confirm", options?.message);
832
905
  validateMessage("confirm", options?.message);
@@ -1178,13 +1251,6 @@ async function runCommand(root, argv, options = {}) {
1178
1251
  await output.drain();
1179
1252
  }
1180
1253
  }
1181
- async function drainAndExit(output, code = 1) {
1182
- try {
1183
- await output.drain();
1184
- } finally {
1185
- process.exit(code);
1186
- }
1187
- }
1188
1254
  function commandErrorExitCode(error) {
1189
1255
  if (error instanceof HackshExitError) return error.exitCode;
1190
1256
  return error instanceof HackshPromptError && error.reason === "cancelled" ? 130 : 1;
@@ -1213,21 +1279,6 @@ function listenForExitSignals() {
1213
1279
  }
1214
1280
  };
1215
1281
  }
1216
- async function reportOutputSetupError(error, options) {
1217
- if (options.onError) {
1218
- await options.onError(error);
1219
- return drainAndExit(createPrintQueue(options.printer ?? { print: () => void 0 }));
1220
- }
1221
- const output = createPrintQueue(options.printer ?? createJsonPrinter({
1222
- stdout: options.stdout ?? process.stdout,
1223
- stderr: options.stderr ?? process.stderr
1224
- }));
1225
- await output.printer.print({
1226
- type: "error",
1227
- error: summarizeError(error)
1228
- });
1229
- return drainAndExit(output);
1230
- }
1231
1282
  async function reportCommandError(error, output) {
1232
1283
  if (error instanceof HackshExitError && error.message === "") return;
1233
1284
  await output.printer.print({
@@ -1240,37 +1291,33 @@ async function reportCommandError(error, output) {
1240
1291
  });
1241
1292
  }
1242
1293
  async function runMain(command, options = {}) {
1243
- let output;
1244
- try {
1245
- output = prepareOutput(options, options.env ?? process.env);
1246
- } catch (error) {
1247
- return reportOutputSetupError(error, options);
1248
- }
1294
+ const output = prepareOutput(options, options.env ?? process.env);
1249
1295
  const signals = listenForExitSignals();
1250
- const exit = (code) => {
1251
- signals.release();
1252
- return drainAndExit(output, signals.exitCode() ?? code);
1253
- };
1296
+ let exitCode;
1254
1297
  try {
1255
- await executeCommand(command, options.argv ?? process.argv.slice(2), output, options.env, signals.signal, () => findPackageVersion(process.argv[1]));
1256
- } catch (error) {
1257
- if (options.onError) {
1258
- await options.onError(error);
1259
- return exit(commandErrorExitCode(error));
1298
+ try {
1299
+ await executeCommand(command, options.argv ?? process.argv.slice(2), output, options.env, signals.signal, () => findPackageVersion(process.argv[1]));
1300
+ } catch (error) {
1301
+ const signalExitCode = signals.exitCode();
1302
+ if (signalExitCode !== void 0 && error === signals.signal.reason) exitCode = signalExitCode;
1303
+ else {
1304
+ if (!(error instanceof HackshError)) throw error;
1305
+ if (options.onError) await options.onError(error);
1306
+ else await reportCommandError(error, output);
1307
+ exitCode = commandErrorExitCode(error);
1308
+ }
1260
1309
  }
1310
+ } finally {
1261
1311
  try {
1262
- await reportCommandError(error, output);
1312
+ await output.drain();
1263
1313
  } finally {
1264
- await exit(commandErrorExitCode(error));
1314
+ signals.release();
1265
1315
  }
1266
- } finally {
1267
- signals.release();
1268
1316
  }
1269
- const signalExitCode = signals.exitCode();
1270
- if (signalExitCode !== void 0) return drainAndExit(output, signalExitCode);
1271
- await output.drain();
1317
+ const code = signals.exitCode() ?? exitCode;
1318
+ if (code !== void 0) process.exit(code);
1272
1319
  }
1273
1320
  //#endregion
1274
- export { HackshExecError, HackshExitError, HackshPromptError, HackshUsageError, HackshValidationError, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage, summarizeUsage };
1321
+ export { HackshError, HackshExecError, HackshExitError, HackshPromptError, HackshUsageError, HackshValidationError, defineCommand, parseArgs, renderUsage, runCommand, runMain, showUsage, summarizeUsage };
1275
1322
 
1276
1323
  //# sourceMappingURL=index.js.map