@kaynooo/cli-pkg 0.7.0 → 0.8.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.
Files changed (3) hide show
  1. package/README.md +11 -4
  2. package/dist/cli.js +1036 -586
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -26,7 +26,19 @@ var __esm = (fn, res, err) => () => {
26
26
  return res;
27
27
  };
28
28
 
29
- // ../../node_modules/.bun/@kaynooo+utils@2.31.0+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
29
+ // ../kit/src/advisory.ts
30
+ function severityRank(severity) {
31
+ return SEVERITIES.indexOf(severity);
32
+ }
33
+ function isIgnored(advisory, ignore) {
34
+ return ignore.includes(advisory.ghsa ?? "") || ignore.includes(String(advisory.id));
35
+ }
36
+ var SEVERITIES;
37
+ var init_advisory = __esm(() => {
38
+ SEVERITIES = ["low", "moderate", "high", "critical"];
39
+ });
40
+
41
+ // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
30
42
  function x(s) {
31
43
  let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
32
44
  if (!/^[a-f0-9]+$/.test(r))
@@ -245,7 +257,7 @@ class e {
245
257
  var m = (s) => s <= 0.04045 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4, p = (s) => s <= 0.0031308 ? 12.92 * s : 1.055 * s ** 0.4166666666666667 - 0.055, i = (s, r) => Math.round(s * 10 ** r) / 10 ** r;
246
258
  var init_chunk_mtetydvc = () => {};
247
259
 
248
- // ../../node_modules/.bun/@kaynooo+utils@2.31.0+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
260
+ // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
249
261
  function h(i, n = globalThis.process?.env ?? {}) {
250
262
  if (n.FORCE_COLOR !== undefined)
251
263
  return b.has(n.FORCE_COLOR);
@@ -287,36 +299,36 @@ var init_chunk_y1msbssb = __esm(() => {
287
299
  t = le();
288
300
  });
289
301
 
290
- // ../../node_modules/.bun/@kaynooo+utils@2.31.0+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/tools/colors.js
302
+ // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/tools/colors.js
291
303
  var init_colors = __esm(() => {
292
304
  init_chunk_y1msbssb();
293
305
  });
294
306
 
295
307
  // ../kit/src/style.ts
296
- import process from "process";
297
- function takesControlCodes(stream, env = process.env) {
308
+ import process2 from "process";
309
+ function takesControlCodes(stream, env = process2.env) {
298
310
  return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
299
311
  }
300
- function paint(stream, env = process.env) {
312
+ function paint(stream, env = process2.env) {
301
313
  return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
302
314
  }
303
- function marked(mark, text, stream, detail, env = process.env) {
315
+ function marked(mark, text, stream, detail, env = process2.env) {
304
316
  const colors = paint(stream, env);
305
317
  const { symbol, word, paint: tint } = MARKS[mark];
306
318
  const head = takesControlCodes(stream, env) ? tint(colors)(symbol) : word;
307
319
  return `${head} ${text}${detail === undefined ? "" : ` ${colors.dim(detail)}`}`;
308
320
  }
309
321
  function ok(text, detail) {
310
- console.log(marked("ok", text, process.stdout, detail));
322
+ console.log(marked("ok", text, process2.stdout, detail));
311
323
  }
312
324
  function note(text, detail) {
313
- console.log(marked("note", text, process.stdout, detail));
325
+ console.log(marked("note", text, process2.stdout, detail));
314
326
  }
315
327
  function warn(text, detail) {
316
- console.error(marked("warn", text, process.stderr, detail));
328
+ console.error(marked("warn", text, process2.stderr, detail));
317
329
  }
318
330
  function problem(text, detail) {
319
- console.error(marked("problem", text, process.stderr, detail));
331
+ console.error(marked("problem", text, process2.stderr, detail));
320
332
  }
321
333
  var MARKS, STATUSES, STATUS_OF;
322
334
  var init_style = __esm(() => {
@@ -338,7 +350,7 @@ var init_style = __esm(() => {
338
350
  });
339
351
 
340
352
  // ../kit/src/format.ts
341
- import process2 from "process";
353
+ import process3 from "process";
342
354
  function formatSeconds(total) {
343
355
  let rest = Math.max(0, Math.round(total));
344
356
  const amounts = UNITS.map(([unit, size]) => {
@@ -383,7 +395,7 @@ function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
383
395
  widths[index] = Math.max(widths[index] ?? 0, textWidth(cell));
384
396
  }
385
397
  const shown = fit === undefined ? cells : narrow(cells, widths, fit);
386
- const colors = paint(process2.stdout);
398
+ const colors = paint(process3.stdout);
387
399
  const lines = shown.map((row) => row.map((cell, index) => {
388
400
  const width = widths[index] ?? 0;
389
401
  const gap = " ".repeat(Math.max(0, width - textWidth(cell)));
@@ -421,6 +433,67 @@ var init_format = __esm(() => {
421
433
  ESCAPE = String.fromCharCode(27);
422
434
  });
423
435
 
436
+ // ../kit/src/help.ts
437
+ import process4 from "process";
438
+ function usageLine(usage, colors = paint(process4.stdout)) {
439
+ return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
440
+ }
441
+ function helpText(usage, options, colors = paint(process4.stdout), { examples = [], replaces, exits = [] } = {}) {
442
+ return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
443
+ `);
444
+ }
445
+ function exitRows(exits, colors = paint(process4.stdout)) {
446
+ if (exits.length === 0)
447
+ return [];
448
+ const width = Math.max(...exits.map(([code]) => String(code).length));
449
+ return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
450
+ }
451
+ function replacesRows(replaces, colors = paint(process4.stdout)) {
452
+ return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
453
+ }
454
+ function wantsHelpJson(env = process4.env) {
455
+ return env.KIT_HELP === "json";
456
+ }
457
+ function description(usage, { examples = [], replaces, exits } = {}, options) {
458
+ const lines = usage.replace(USAGE_PREFIX, "").split(`
459
+ `);
460
+ const first = lines[0];
461
+ const colon = first.indexOf(": ");
462
+ const prose = lines[lines.indexOf("") + 1] ?? "";
463
+ const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
464
+ return {
465
+ synopsis: colon === -1 ? first : first.slice(0, colon),
466
+ summary: colon === -1 ? fallback : first.slice(colon + 2),
467
+ ...replaces === undefined ? {} : { replaces },
468
+ examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] }),
469
+ ...exits === undefined ? {} : { exits: exits.map(([code, meaning]) => ({ code, meaning })) },
470
+ ...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
471
+ };
472
+ }
473
+ function exampleRows(examples, colors = paint(process4.stdout)) {
474
+ if (examples.length === 0)
475
+ return [];
476
+ return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
477
+ }
478
+ function optionRows(options, colors = paint(process4.stdout)) {
479
+ const rows = Object.entries(options).map(([name, option]) => {
480
+ const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
481
+ const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
482
+ const placeholder = option.optional === undefined ? named : `[${named}]`;
483
+ const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
484
+ return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
485
+ });
486
+ const width = Math.max(0, ...rows.map((row) => row.width));
487
+ return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
488
+ }
489
+ var OPTIONAL, USAGE_PREFIX, FULL_STOP;
490
+ var init_help = __esm(() => {
491
+ init_style();
492
+ OPTIONAL = /\[[^\]]+\]/g;
493
+ USAGE_PREFIX = /^usage:\s*/i;
494
+ FULL_STOP = /\.$/;
495
+ });
496
+
424
497
  // ../kit/src/text.ts
425
498
  function distance(a, b) {
426
499
  let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
@@ -444,8 +517,51 @@ var init_text = __esm(() => {
444
517
  ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
445
518
  });
446
519
 
520
+ // ../kit/src/input.ts
521
+ import { fstatSync } from "fs";
522
+ import process5 from "process";
523
+ function wholeNumber(value, name, fallback, min = 1) {
524
+ if (value === undefined)
525
+ return fallback;
526
+ const number = Number(value);
527
+ if (!WHOLE.test(value) || !Number.isSafeInteger(number) || number < min)
528
+ badUsage(`${name.startsWith("<") ? name : `--${name}`} takes a whole number from ${min}, not ${value}`);
529
+ return number;
530
+ }
531
+ function duration(value, name, fallback, unit = "s") {
532
+ if (value === undefined)
533
+ return fallback;
534
+ const match = DURATION.exec(value.trim());
535
+ const ms = match === null ? 0 : Number(match[1]) * UNIT_MS[match[2] ?? unit];
536
+ if (ms < 1)
537
+ badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
538
+ return ms;
539
+ }
540
+ function isStdinPiped() {
541
+ const stat = fstatSync(0);
542
+ return stat.isFIFO() || stat.isFile();
543
+ }
544
+ async function readInput(path, { noStdin = "nothing is piped on stdin" } = {}) {
545
+ if (path === "-") {
546
+ if (process5.stdin.isTTY)
547
+ fail(noStdin);
548
+ return Bun.stdin.text();
549
+ }
550
+ const file = Bun.file(path);
551
+ if (!await file.exists())
552
+ fail(`no file ${path}`);
553
+ return file.text();
554
+ }
555
+ var WHOLE, DURATION, UNIT_MS;
556
+ var init_input = __esm(() => {
557
+ init_args();
558
+ WHOLE = /^\d+$/;
559
+ DURATION = /^(\d+(?:\.\d+)?)(ms|[smhd])?$/;
560
+ UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
561
+ });
562
+
447
563
  // ../kit/src/args.ts
448
- import process3 from "process";
564
+ import process6 from "process";
449
565
  import { parseArgs } from "util";
450
566
  function jsonOption(holds) {
451
567
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
@@ -459,52 +575,15 @@ function badUsage(message) {
459
575
 
460
576
  class Completing {
461
577
  options;
462
- constructor(options) {
578
+ positionals;
579
+ candidates;
580
+ constructor(options, positionals = [], candidates = {}) {
463
581
  this.options = options;
582
+ this.positionals = positionals;
583
+ this.candidates = candidates;
464
584
  }
465
585
  }
466
- function usageLine(usage, colors = paint(process3.stdout)) {
467
- return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
468
- }
469
- function helpText(usage, options, colors = paint(process3.stdout), { examples = [], replaces } = {}) {
470
- return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
471
- `);
472
- }
473
- function replacesRows(replaces, colors = paint(process3.stdout)) {
474
- return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
475
- }
476
- function wantsHelpJson(env = process3.env) {
477
- return env.KIT_HELP === "json";
478
- }
479
- function description(usage, { examples = [], replaces } = {}) {
480
- const lines = usage.replace(USAGE_PREFIX, "").split(`
481
- `);
482
- const first = lines[0];
483
- const colon = first.indexOf(": ");
484
- const prose = lines[lines.indexOf("") + 1] ?? "";
485
- const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
486
- return {
487
- synopsis: colon === -1 ? first : first.slice(0, colon),
488
- summary: colon === -1 ? fallback : first.slice(colon + 2),
489
- ...replaces === undefined ? {} : { replaces },
490
- examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
491
- };
492
- }
493
- function exampleRows(examples, colors = paint(process3.stdout)) {
494
- if (examples.length === 0)
495
- return [];
496
- return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
497
- }
498
- function optionRows(options, colors = paint(process3.stdout)) {
499
- const rows = Object.entries(options).map(([name, option]) => {
500
- const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
501
- const value = option.type === "string" ? ` ${colors.dim("<value>")}` : "";
502
- return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? " <value>".length : 0), description: option.description ?? "" };
503
- });
504
- const width = Math.max(0, ...rows.map((row) => row.width));
505
- return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
506
- }
507
- function applyOutput(color, plain, env = process3.env) {
586
+ function applyOutput(color, plain, env = process6.env) {
508
587
  const when = plain === true ? "never" : color ?? "auto";
509
588
  if (when === "auto")
510
589
  return;
@@ -521,14 +600,13 @@ function applyOutput(color, plain, env = process3.env) {
521
600
  function parse(args, usage, options, settings = {}) {
522
601
  const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
523
602
  const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
603
+ const listing = listed(all, settings.allowNegative === true);
524
604
  if (completion.active)
525
- throw new Completing(all);
526
- const separator = settings.rest === true ? args.indexOf("--") : -1;
527
- const own = separator === -1 ? args : args.slice(0, separator);
528
- const rest = separator === -1 ? undefined : args.slice(separator + 1);
605
+ throw new Completing(listing, settings.positionals, settings.candidates);
606
+ const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
529
607
  let parsed;
530
608
  try {
531
- parsed = parseArgs({ args: joinDashValues(own, all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
609
+ parsed = parseArgs({ args: joinDashValues(withBare(withAliases(own, all), all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
532
610
  } catch (error) {
533
611
  const message = parseError(error);
534
612
  throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
@@ -536,12 +614,96 @@ function parse(args, usage, options, settings = {}) {
536
614
  const values = parsed.values;
537
615
  applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
538
616
  if (values.help === true) {
539
- console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, all, undefined, settings));
617
+ console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings, listing)) : helpText(usage, listing, undefined, settings));
540
618
  throw new HelpShown;
541
619
  }
620
+ checkChoices(parsed.values, all);
621
+ checkConflicts(parsed.values, all);
542
622
  if (settings.positionals !== undefined)
543
623
  checkPositionals(parsed.positionals, settings.positionals);
544
- return { ...parsed, positionals: parsed.positionals, rest };
624
+ return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
625
+ }
626
+ function splitRest(args, passes) {
627
+ const separator = passes ? args.indexOf("--") : -1;
628
+ return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
629
+ }
630
+ function splitTrailing(args, options, count) {
631
+ const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
632
+ let seen = 0;
633
+ if (count === 0)
634
+ return { own: args, rest: undefined };
635
+ for (let at = 0;at < args.length; at++) {
636
+ const word = args[at];
637
+ if (word === "--")
638
+ break;
639
+ if (word.startsWith("-")) {
640
+ if (takesValue(word))
641
+ at++;
642
+ continue;
643
+ }
644
+ seen++;
645
+ if (seen === count)
646
+ return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
647
+ }
648
+ return { own: args, rest: undefined };
649
+ }
650
+ function listed(options, allowNegative) {
651
+ if (!allowNegative)
652
+ return options;
653
+ return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
654
+ if (option.type !== "boolean" || option.negative === undefined)
655
+ return [[name, option]];
656
+ return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
657
+ }));
658
+ }
659
+ function withAliases(args, options) {
660
+ const named = new Map(Object.entries(options).flatMap(([name, { aliases = [] }]) => aliases.map((alias) => [`--${alias}`, `--${name}`])));
661
+ if (named.size === 0)
662
+ return args;
663
+ const separator = args.indexOf("--");
664
+ const own = separator === -1 ? args : args.slice(0, separator);
665
+ return [...own.map((arg) => {
666
+ const at = arg.indexOf("=");
667
+ const current = named.get(at === -1 ? arg : arg.slice(0, at));
668
+ return current === undefined ? arg : `${current}${at === -1 ? "" : arg.slice(at)}`;
669
+ }), ...separator === -1 ? [] : args.slice(separator)];
670
+ }
671
+ function withBare(args, options) {
672
+ const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
673
+ if (bare.size === 0)
674
+ return args;
675
+ const separator = args.indexOf("--");
676
+ const own = separator === -1 ? args : args.slice(0, separator);
677
+ return [...own.flatMap((arg, index) => {
678
+ const optional = bare.get(arg);
679
+ const next = own[index + 1];
680
+ const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
681
+ return optional === undefined || value ? [arg] : [arg, optional.bare];
682
+ }), ...separator === -1 ? [] : args.slice(separator)];
683
+ }
684
+ function checkConflicts(values, options) {
685
+ const given = (name) => values[name] !== undefined && values[name] !== false;
686
+ for (const [name, { conflicts = [] }] of Object.entries(options)) {
687
+ const other = given(name) ? conflicts.find(given) : undefined;
688
+ if (other !== undefined)
689
+ badUsage(`--${name} cannot be used with --${other}`);
690
+ }
691
+ }
692
+ function checkChoices(values, options) {
693
+ for (const [name, option] of Object.entries(options)) {
694
+ if (option.choices === undefined)
695
+ continue;
696
+ const given = values[name];
697
+ for (const value of Array.isArray(given) ? given : [given]) {
698
+ if (typeof value === "string")
699
+ oneOf(value, name, option.choices);
700
+ }
701
+ }
702
+ }
703
+ function oneOf(value, name, choices) {
704
+ if (!choices.includes(value))
705
+ badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
706
+ return value;
545
707
  }
546
708
  function checkPositionals(given, names) {
547
709
  const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
@@ -589,28 +751,14 @@ function nearestOption(message, options, allowNegative) {
589
751
  const meant = closest(word, known.map((flag) => flag.replace(DASHES, "")));
590
752
  return meant === undefined ? undefined : `did you mean ${known.find((flag) => flag.replace(DASHES, "") === meant)}?`;
591
753
  }
592
- function wholeNumber(value, name, fallback, min = 1) {
593
- if (value === undefined)
594
- return fallback;
595
- const number = Number(value);
596
- if (!WHOLE.test(value) || !Number.isSafeInteger(number) || number < min)
597
- badUsage(`${name.startsWith("<") ? name : `--${name}`} takes a whole number from ${min}, not ${value}`);
598
- return number;
599
- }
600
- function duration(value, name, fallback, unit = "s") {
601
- if (value === undefined)
602
- return fallback;
603
- const match = DURATION.exec(value.trim());
604
- const ms = match === null ? 0 : Number(match[1]) * UNIT_MS[match[2] ?? unit];
605
- if (ms < 1)
606
- badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
607
- return ms;
608
- }
609
- var Exit, UsageError, HelpShown, completion, OPTIONAL, USAGE_PREFIX, FULL_STOP, DASHES, OUTPUT_OPTIONS, UNKNOWN_OPTION, WHOLE, DURATION, UNIT_MS;
754
+ var DASHES, Exit, UsageError, HelpShown, completion, OUTPUT_OPTIONS, UNKNOWN_OPTION;
610
755
  var init_args = __esm(() => {
611
756
  init_format();
612
- init_style();
757
+ init_help();
613
758
  init_text();
759
+ init_help();
760
+ init_input();
761
+ DASHES = /^-+/;
614
762
  Exit = class Exit extends Error {
615
763
  code;
616
764
  hint;
@@ -631,18 +779,11 @@ var init_args = __esm(() => {
631
779
  }
632
780
  };
633
781
  completion = { active: false };
634
- OPTIONAL = /\[[^\]]+\]/g;
635
- USAGE_PREFIX = /^usage:\s*/i;
636
- FULL_STOP = /\.$/;
637
- DASHES = /^-+/;
638
782
  OUTPUT_OPTIONS = {
639
783
  color: { type: "string", description: "auto (on a terminal alone), always or never" },
640
784
  plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
641
785
  };
642
786
  UNKNOWN_OPTION = /^unknown option '(-[^']+)'/;
643
- WHOLE = /^\d+$/;
644
- DURATION = /^(\d+(?:\.\d+)?)(ms|[smhd])?$/;
645
- UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
646
787
  });
647
788
 
648
789
  // ../kit/src/chart.ts
@@ -653,7 +794,7 @@ var init_chart = __esm(() => {
653
794
 
654
795
  // ../kit/src/process.ts
655
796
  import { constants } from "os";
656
- import process4 from "process";
797
+ import process7 from "process";
657
798
  function exitCodeOf(code, signal) {
658
799
  if (code !== null)
659
800
  return code;
@@ -673,7 +814,7 @@ function holdSignals(cleanup) {
673
814
  },
674
815
  release: () => {
675
816
  for (const signal of SIGNALS)
676
- process4.off(signal, onSignal);
817
+ process7.off(signal, onSignal);
677
818
  }
678
819
  };
679
820
  function onSignal(signal) {
@@ -686,10 +827,10 @@ function holdSignals(cleanup) {
686
827
  return;
687
828
  held.release();
688
829
  cleanup(signal);
689
- process4.kill(process4.pid, signal);
830
+ process7.kill(process7.pid, signal);
690
831
  }
691
832
  for (const signal of SIGNALS)
692
- process4.on(signal, onSignal);
833
+ process7.on(signal, onSignal);
693
834
  return held;
694
835
  }
695
836
  async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal, maxBytes = Infinity, onChunk, onStart } = {}) {
@@ -703,7 +844,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
703
844
  return;
704
845
  try {
705
846
  if (grouped)
706
- process4.kill(-proc.pid, killing);
847
+ process7.kill(-proc.pid, killing);
707
848
  else
708
849
  proc.kill(killing);
709
850
  } catch {}
@@ -776,8 +917,8 @@ var init_process = __esm(() => {
776
917
  });
777
918
 
778
919
  // ../kit/src/progress.ts
779
- import process5 from "process";
780
- function liveRegion(render, stream = process5.stderr, env = process5.env) {
920
+ import process8 from "process";
921
+ function liveRegion(render, stream = process8.stderr, env = process8.env) {
781
922
  if (!takesControlCodes(stream, env))
782
923
  return { redraw: () => {
783
924
  return;
@@ -818,11 +959,11 @@ function liveRegion(render, stream = process5.stderr, env = process5.env) {
818
959
  clear();
819
960
  restore();
820
961
  release();
821
- process5.off("exit", onExit);
962
+ process8.off("exit", onExit);
822
963
  signals.release();
823
964
  }
824
965
  timer.unref();
825
- process5.on("exit", onExit);
966
+ process8.on("exit", onExit);
826
967
  return {
827
968
  redraw: draw,
828
969
  print: (text) => {
@@ -861,14 +1002,14 @@ function cutToWidth(line, width) {
861
1002
  return `${kept}${RESET_STYLE}${linked ? CLOSE_LINK : ""}`;
862
1003
  }
863
1004
  function swallowInput() {
864
- const input = process5.stdin;
1005
+ const input = process8.stdin;
865
1006
  if (!input.isTTY)
866
1007
  return () => {
867
1008
  return;
868
1009
  };
869
1010
  const onData = (data) => {
870
1011
  if (data.includes(CTRL_C))
871
- process5.kill(process5.pid, "SIGINT");
1012
+ process8.kill(process8.pid, "SIGINT");
872
1013
  };
873
1014
  input.setRawMode(true);
874
1015
  input.on("data", onData);
@@ -884,7 +1025,7 @@ function swallowInput() {
884
1025
  };
885
1026
  }
886
1027
  function interceptOutput(clear) {
887
- const targets = [...CONSOLE_METHODS.map((name) => [console, name]), [process5.stdout, "write"], [process5.stderr, "write"]];
1028
+ const targets = [...CONSOLE_METHODS.map((name) => [console, name]), [process8.stdout, "write"], [process8.stderr, "write"]];
888
1029
  const wrapped = targets.map(([target, name]) => {
889
1030
  const original = Reflect.get(target, name);
890
1031
  const wrapper = (...args) => {
@@ -928,10 +1069,65 @@ var init_ci = __esm(() => {
928
1069
  init_tool();
929
1070
  });
930
1071
 
1072
+ // ../kit/src/logs.ts
1073
+ var DAY_MS, CLI_FOLDER = "kaynooo-cli";
1074
+ var init_logs = __esm(() => {
1075
+ DAY_MS = 24 * 60 * 60 * 1000;
1076
+ });
1077
+
1078
+ // ../kit/src/usage.ts
1079
+ import { appendFileSync, mkdirSync } from "fs";
1080
+ import { homedir } from "os";
1081
+ import { dirname, isAbsolute, join } from "path";
1082
+ import process9 from "process";
1083
+ function noteUsageError(message) {
1084
+ usageError = message;
1085
+ }
1086
+ function stateHome(env = process9.env) {
1087
+ const dir = env.XDG_STATE_HOME;
1088
+ return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
1089
+ }
1090
+ function usagePath(env = process9.env) {
1091
+ return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
1092
+ }
1093
+ function usageRecorded(env = process9.env) {
1094
+ const off = env.KAYNOOO_CLI_NO_USAGE;
1095
+ return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
1096
+ }
1097
+ function flagNames(argv) {
1098
+ const end = argv.indexOf("--");
1099
+ const names = (end === -1 ? argv : argv.slice(0, end)).filter((word) => word.startsWith("-") && word !== "-").map((word) => word.includes("=") ? word.slice(0, word.indexOf("=")) : word);
1100
+ return [...new Set(names)].filter((name) => name.length <= LONGEST_NAME && FLAG_NAME.test(name));
1101
+ }
1102
+ function usageEntry(bin, command, argv, code, now = new Date) {
1103
+ const unknown = code === 2 && usageError !== undefined ? UNKNOWN_OPTION2.exec(usageError)?.[1] : undefined;
1104
+ usageError = undefined;
1105
+ return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
1106
+ }
1107
+ function recordUsage(entry, env = process9.env) {
1108
+ if (!usageRecorded(env))
1109
+ return;
1110
+ try {
1111
+ const path = usagePath(env);
1112
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
1113
+ appendFileSync(path, `${JSON.stringify(entry)}
1114
+ `, { mode: 384 });
1115
+ } catch {}
1116
+ }
1117
+ var UNKNOWN_OPTION2, FLAG_NAME, LONGEST_NAME = 40, usageError;
1118
+ var init_usage = __esm(() => {
1119
+ init_logs();
1120
+ UNKNOWN_OPTION2 = /unknown option '(--[a-z][a-z0-9-]*)[=']/;
1121
+ FLAG_NAME = /^(?:--[a-z][a-z0-9-]*|-[a-zA-Z])$/;
1122
+ });
1123
+
931
1124
  // ../kit/src/cli.ts
932
- import process6 from "process";
933
- async function commandRows(commands, colors) {
934
- const width = Math.max(...Object.keys(commands).map((name) => name.length));
1125
+ import { readFileSync, realpathSync } from "fs";
1126
+ import { dirname as dirname2, join as join2 } from "path";
1127
+ import process10 from "process";
1128
+ async function commandRows(all, colors) {
1129
+ const commands = shown(all);
1130
+ const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
935
1131
  const groups = new Map([["Commands:", []]]);
936
1132
  const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
937
1133
  for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
@@ -942,6 +1138,9 @@ async function commandRows(commands, colors) {
942
1138
  }
943
1139
  return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
944
1140
  }
1141
+ function shown(commands) {
1142
+ return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
1143
+ }
945
1144
  function takeOutputFlags(argv) {
946
1145
  let index = 0;
947
1146
  for (;index < argv.length; index++) {
@@ -973,7 +1172,7 @@ async function expand(scope, words) {
973
1172
  }
974
1173
  return { ...scope, commands, lazy: [] };
975
1174
  }
976
- async function help({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
1175
+ async function help2({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
977
1176
  const colors = paint(stream);
978
1177
  const prefix = path.join(" ");
979
1178
  const summary = await resolveSummary(lazySummary);
@@ -1000,8 +1199,8 @@ async function help({ path, header, summary: lazySummary, commands, defaultComma
1000
1199
  `);
1001
1200
  }
1002
1201
  function printError(prefix, message, hint, cause) {
1003
- const colors = paint(process6.stderr);
1004
- const styled = takesControlCodes(process6.stderr);
1202
+ const colors = paint(process10.stderr);
1203
+ const styled = takesControlCodes(process10.stderr);
1005
1204
  const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
1006
1205
  const under = (label, text, tint) => text === undefined ? "" : `
1007
1206
  ${styled ? tint(label) : `${label}:`} ${text}`;
@@ -1020,6 +1219,8 @@ function reportThrown(prefix, error, redact = (text) => text, json = false) {
1020
1219
  const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
1021
1220
  const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
1022
1221
  const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
1222
+ if (error instanceof UsageError)
1223
+ noteUsageError(error.message);
1023
1224
  const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
1024
1225
  report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
1025
1226
  return error instanceof Exit ? error.code : 1;
@@ -1033,12 +1234,20 @@ async function dispatch(level, typed, { version, redact }) {
1033
1234
  return reportThrown(prefix, error);
1034
1235
  }
1035
1236
  const [name, ...args] = argv;
1237
+ const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
1238
+ if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
1239
+ try {
1240
+ return await fallback.run([]);
1241
+ } catch (error) {
1242
+ return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
1243
+ }
1244
+ }
1036
1245
  if (name === undefined && level.path.length > 1) {
1037
- console.error(await help(await expand(level, [...level.path.slice(1), ...argv]), process6.stderr));
1246
+ console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process10.stderr));
1038
1247
  return 2;
1039
1248
  }
1040
1249
  if (name === undefined || name === "help" || name === "--help" || name === "-h") {
1041
- console.log(await help(await expand(level, [...level.path.slice(1), ...argv]), process6.stdout));
1250
+ console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process10.stdout));
1042
1251
  return 0;
1043
1252
  }
1044
1253
  if (level.path.length === 1 && (name === "--version" || name === "-v")) {
@@ -1047,6 +1256,7 @@ async function dispatch(level, typed, { version, redact }) {
1047
1256
  }
1048
1257
  let command = level.commands[level.aliases?.[name] ?? name];
1049
1258
  let rest = args;
1259
+ let ran = name;
1050
1260
  if (command === undefined && level.lazy.length > 0) {
1051
1261
  level = await expand(level, [...level.path.slice(1), ...argv]);
1052
1262
  command = level.commands[level.aliases?.[name] ?? name];
@@ -1054,38 +1264,75 @@ async function dispatch(level, typed, { version, redact }) {
1054
1264
  if (command === undefined && level.defaultCommand !== undefined) {
1055
1265
  command = level.commands[level.defaultCommand];
1056
1266
  rest = argv;
1267
+ ran = level.defaultCommand;
1057
1268
  }
1058
1269
  if (command === undefined) {
1059
- const meant = closest(name, Object.keys(level.commands));
1270
+ const meant = closest(name, Object.keys(shown(level.commands)));
1060
1271
  printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
1061
1272
  return 2;
1062
1273
  }
1063
1274
  if ("commands" in command) {
1064
1275
  const { summary } = command;
1065
- return dispatch({ path: [...level.path, name], header: (colors, text) => `${colors.bold(`${prefix} ${name}`)} ${text}`, summary, ...scopeOf(command.commands, command.extraCommands, command.aliases), defaultCommand: undefined, examples: command.examples, replaces: undefined }, rest, { version, redact });
1276
+ return dispatch({ path: [...level.path, name], header: (colors, text) => `${colors.bold(`${prefix} ${name}`)} ${text}`, summary, ...scopeOf(command.commands, command.extraCommands, command.aliases), defaultCommand: undefined, runBare: false, examples: command.examples, replaces: undefined }, rest, { version, redact });
1066
1277
  }
1067
1278
  try {
1068
1279
  return await command.run(rest);
1069
1280
  } catch (error) {
1070
- return reportThrown(`${prefix} ${name}`, error, redact, wantsJson(rest));
1281
+ return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
1071
1282
  }
1072
1283
  }
1073
- async function optionsOf(run) {
1284
+ async function completingOf(run) {
1074
1285
  completion.active = true;
1075
1286
  try {
1076
1287
  await run([]);
1077
1288
  } catch (error) {
1078
1289
  if (error instanceof Completing)
1079
- return error.options;
1290
+ return error;
1080
1291
  } finally {
1081
1292
  completion.active = false;
1082
1293
  }
1083
- return {};
1294
+ return new Completing({});
1295
+ }
1296
+ async function optionsOf(run) {
1297
+ return (await completingOf(run)).options;
1298
+ }
1299
+ function candidateLine(candidate) {
1300
+ return typeof candidate === "string" ? candidate : candidate.join("\t");
1301
+ }
1302
+ function positionalBeingTyped(before, { options, positionals }) {
1303
+ const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
1304
+ let index = 0;
1305
+ let at = 0;
1306
+ while (at < before.length) {
1307
+ const word = before[at];
1308
+ if (word === "--") {
1309
+ index += before.length - at - 1;
1310
+ break;
1311
+ }
1312
+ if (!word.startsWith("-"))
1313
+ index++;
1314
+ else if (takesValue(word))
1315
+ at++;
1316
+ at++;
1317
+ }
1318
+ const last = positionals.at(-1);
1319
+ if (index < positionals.length)
1320
+ return positionals[index];
1321
+ return last?.replace("]", "").endsWith("...") === true ? last : undefined;
1084
1322
  }
1085
1323
  async function leafCandidates(run, words) {
1086
- if (!(words.at(-1) ?? "").startsWith("-"))
1087
- return [FILES];
1088
- return Object.entries(await optionsOf(run)).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
1324
+ const completing = await completingOf(run);
1325
+ if ((words.at(-1) ?? "").startsWith("-"))
1326
+ return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
1327
+ const flag = words.at(-2);
1328
+ const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
1329
+ if (option?.type === "string") {
1330
+ const values = option.choices === undefined ? await option.candidates?.() : option.choices;
1331
+ return values === undefined ? [FILES] : values.map(candidateLine);
1332
+ }
1333
+ const name = positionalBeingTyped(words.slice(0, -1), completing);
1334
+ const values = name === undefined ? undefined : await completing.candidates[name]?.();
1335
+ return values === undefined ? [FILES] : values.map(candidateLine);
1089
1336
  }
1090
1337
  async function candidates(scope, defaultCommand, words, before = []) {
1091
1338
  const [word = "", ...rest] = words;
@@ -1096,8 +1343,13 @@ async function candidates(scope, defaultCommand, words, before = []) {
1096
1343
  ({ commands, aliases } = await expand(scope, [...before, ...words]));
1097
1344
  command = commands[aliases?.[word] ?? word];
1098
1345
  }
1099
- if (listing)
1100
- return Promise.all(Object.entries(commands).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
1346
+ if (listing) {
1347
+ const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
1348
+ const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
1349
+ const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
1350
+ const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
1351
+ return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
1352
+ }
1101
1353
  if (command === undefined) {
1102
1354
  const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
1103
1355
  return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
@@ -1109,23 +1361,56 @@ async function printCandidates(found) {
1109
1361
  console.log(line);
1110
1362
  return 0;
1111
1363
  }
1112
- async function runCli(cli, argv = process6.argv.slice(2)) {
1364
+ function packageVersion(entry = Bun.main) {
1365
+ for (let dir = dirname2(realpathSync(entry));; dir = dirname2(dir)) {
1366
+ try {
1367
+ const { version } = JSON.parse(readFileSync(join2(dir, "package.json"), "utf8"));
1368
+ if (typeof version === "string")
1369
+ return version;
1370
+ } catch {}
1371
+ if (dirname2(dir) === dir)
1372
+ return "0.0.0";
1373
+ }
1374
+ }
1375
+ async function runCli(cli, argv = process10.argv.slice(2)) {
1376
+ const version = cli.version ?? packageVersion();
1113
1377
  if (argv[0] === COMPLETE)
1114
1378
  return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
1115
- return dispatch({ path: [cli.name], header: (colors, about) => `${colors.bold(cli.name)} ${colors.dim(cli.version)} ${about}`, summary: cli.about, ...scopeOf(cli.commands, cli.extraCommands, cli.aliases), defaultCommand: cli.defaultCommand, examples: cli.examples, replaces: cli.replaces }, argv, { version: cli.version, redact: cli.redact });
1379
+ const code = await dispatch({ path: [cli.name], header: (colors, about) => `${colors.bold(cli.name)} ${colors.dim(version)} ${about}`, summary: cli.about, ...scopeOf(cli.commands, cli.extraCommands, cli.aliases), defaultCommand: cli.defaultCommand, runBare: cli.runBare === true, examples: cli.examples, replaces: cli.replaces }, argv, { version, redact: cli.redact });
1380
+ if (cli.usage !== false) {
1381
+ try {
1382
+ recordUsage(usageEntry(cli.name, commandPath(scopeOf(cli.commands, cli.extraCommands, cli.aliases), argv), argv, code));
1383
+ } catch {}
1384
+ }
1385
+ return code;
1386
+ }
1387
+ function commandPath(scope, argv) {
1388
+ const path = [];
1389
+ let level = scope;
1390
+ for (const word of argv.filter((each) => !each.startsWith("-"))) {
1391
+ const name = level.aliases !== undefined && Object.hasOwn(level.aliases, word) ? level.aliases[word] ?? word : word;
1392
+ const command = Object.hasOwn(level.commands, name) ? level.commands[name] : undefined;
1393
+ if (command === undefined)
1394
+ break;
1395
+ path.push(word);
1396
+ if (!("commands" in command))
1397
+ break;
1398
+ level = scopeOf(command.commands, command.extraCommands, command.aliases);
1399
+ }
1400
+ return path.length === 0 ? undefined : path.join(" ");
1116
1401
  }
1117
1402
  var LEVEL_OPTIONS, TOP_OPTIONS, COMPLETE = "__complete", FILES = "__files__";
1118
1403
  var init_cli = __esm(() => {
1119
1404
  init_args();
1120
1405
  init_style();
1121
1406
  init_text();
1407
+ init_usage();
1122
1408
  LEVEL_OPTIONS = {
1123
1409
  ...OUTPUT_OPTIONS,
1124
1410
  help: { type: "boolean", short: "h", description: "show this help" }
1125
1411
  };
1126
1412
  TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
1127
1413
  });
1128
-
1129
1414
  // ../kit/src/config.ts
1130
1415
  var init_config = __esm(() => {
1131
1416
  init_args();
@@ -1140,7 +1425,7 @@ var init_git = __esm(() => {
1140
1425
  // ../kit/src/limit.ts
1141
1426
  function limitOptions(fallback) {
1142
1427
  return {
1143
- limit: { type: "string", description: `show at most this many rows, the rest counted (${fallback})` },
1428
+ limit: { type: "string", short: "n", description: `show at most this many rows, the rest counted (${fallback})` },
1144
1429
  full: { type: "boolean", description: "show every row" }
1145
1430
  };
1146
1431
  }
@@ -1162,12 +1447,6 @@ var init_limit = __esm(() => {
1162
1447
  init_args();
1163
1448
  });
1164
1449
 
1165
- // ../kit/src/logs.ts
1166
- var DAY_MS;
1167
- var init_logs = __esm(() => {
1168
- DAY_MS = 24 * 60 * 60 * 1000;
1169
- });
1170
-
1171
1450
  // ../kit/src/secrets.ts
1172
1451
  var SECRET_WORDS;
1173
1452
  var init_secrets = __esm(() => {
@@ -1182,8 +1461,33 @@ var init_sessions = __esm(() => {
1182
1461
  ACCOUNT_DIRS = new Glob("*/sessions");
1183
1462
  });
1184
1463
 
1464
+ // ../kit/src/size.ts
1465
+ var init_size = () => {};
1466
+
1467
+ // ../kit/src/user-config.ts
1468
+ import { existsSync } from "fs";
1469
+ import { homedir as homedir2 } from "os";
1470
+ import { join as join3 } from "path";
1471
+ import process11 from "process";
1472
+ function configBase(where = {}) {
1473
+ const xdg = where.xdg ?? (where.home === undefined ? process11.env.XDG_CONFIG_HOME : undefined);
1474
+ return xdg !== undefined && xdg !== "" ? xdg : join3(where.home ?? homedir2(), ".config");
1475
+ }
1476
+ function userConfigPath(bin, where = {}) {
1477
+ return join3(configBase(where), "kaynooo-cli", `${bin}.json`);
1478
+ }
1479
+ function userConfigReadPath(bin, legacy = [], where = {}) {
1480
+ const current = userConfigPath(bin, where);
1481
+ if (existsSync(current))
1482
+ return current;
1483
+ const base = configBase(where);
1484
+ return legacy.map((parts) => join3(base, ...parts)).find((path) => existsSync(path)) ?? current;
1485
+ }
1486
+ var init_user_config = () => {};
1487
+
1185
1488
  // ../kit/src/index.ts
1186
1489
  var init_src = __esm(() => {
1490
+ init_advisory();
1187
1491
  init_args();
1188
1492
  init_chart();
1189
1493
  init_ci();
@@ -1196,276 +1500,70 @@ var init_src = __esm(() => {
1196
1500
  init_process();
1197
1501
  init_secrets();
1198
1502
  init_sessions();
1503
+ init_size();
1199
1504
  init_style();
1200
1505
  init_text();
1201
1506
  init_tool();
1507
+ init_usage();
1508
+ init_user_config();
1202
1509
  });
1203
1510
 
1204
- // src/registry.ts
1205
- function isPublic(registry) {
1206
- return registry.url.replace(TRAILING_SLASHES, "") === PUBLIC_REGISTRY;
1511
+ // src/semver.ts
1512
+ function isRange(text) {
1513
+ if (text.trim() === "")
1514
+ return true;
1515
+ return text.split(OR).every((alternative) => {
1516
+ const words = alternative.trim().split(WHITESPACE);
1517
+ return words.every((word, index) => word === "-" ? index > 0 && index < words.length - 1 : COMPARATOR.test(word));
1518
+ });
1207
1519
  }
1208
- function unreachable(message) {
1209
- return fail(`could not check: ${message}`, 2);
1520
+ function compare(version, word) {
1521
+ const bound = BOUND.exec(word);
1522
+ if (bound === null)
1523
+ return;
1524
+ const order = Bun.semver.order(version, bound[2] ?? "");
1525
+ const operator = bound[1] ?? "=";
1526
+ if (operator === "<")
1527
+ return order < 0;
1528
+ if (operator === "<=")
1529
+ return order <= 0;
1530
+ if (operator === ">")
1531
+ return order > 0;
1532
+ return operator === ">=" ? order >= 0 : order === 0;
1210
1533
  }
1211
- function requestInit(token) {
1212
- return { headers: { accept: "application/json", ...token === undefined ? {} : { authorization: `Bearer ${token}` } }, signal: AbortSignal.timeout(REQUEST_TIMEOUT) };
1534
+ function inRange(version, range) {
1535
+ if (Bun.semver.satisfies(version, range))
1536
+ return true;
1537
+ if (!PRERELEASE.test(version))
1538
+ return false;
1539
+ return range.split(OR).some((alternative) => alternative.trim().split(WHITESPACE).every((word) => compare(version, word) ?? Bun.semver.satisfies(version, word)));
1213
1540
  }
1214
- async function get(fetcher, url, token) {
1215
- try {
1216
- return await fetcher(url, requestInit(token));
1217
- } catch (error) {
1218
- return unreachable(`cannot reach ${new URL(url).host}: ${error instanceof Error ? error.message : String(error)}`);
1219
- }
1220
- }
1221
- function isValidName(name) {
1222
- return PACKAGE_NAME.test(name) && name.length <= 214;
1223
- }
1224
- async function fetchPackument(name, registry, fetcher = fetch) {
1225
- if (!isValidName(name))
1226
- fail(`${name} is not a valid npm package name`);
1227
- const response = await get(fetcher, `${registry.url.replace(TRAILING_SLASHES, "")}/${encodeURIComponent(name).replace("%40", "@").replace("%2F", "%2f")}`, registry.token);
1228
- if (response.status === 404)
1229
- return;
1230
- if (!response.ok)
1231
- unreachable(`${new URL(registry.url).host} answered ${response.status} for ${name}`);
1232
- let packument;
1233
- try {
1234
- packument = await response.json();
1235
- } catch {
1236
- return unreachable(`${new URL(registry.url).host} answered ${name} with something that is not JSON`);
1237
- }
1238
- if (packument?.name !== name)
1239
- unreachable(`${new URL(registry.url).host} answered ${name} with the metadata of ${String(packument?.name)}`);
1240
- return packument;
1241
- }
1242
- async function fetchDownloads(name, fetcher = fetch) {
1243
- const response = await get(fetcher, `${DOWNLOADS_API}/${encodeURIComponent(name)}`);
1244
- if (!response.ok)
1245
- return;
1246
- try {
1247
- return (await response.json()).downloads;
1248
- } catch {
1249
- return;
1250
- }
1251
- }
1252
- var PUBLIC_REGISTRY = "https://registry.npmjs.org", DOWNLOADS_API = "https://api.npmjs.org/downloads/point/last-week", TRAILING_SLASHES, REQUEST_TIMEOUT = 1e4, PACKAGE_NAME;
1253
- var init_registry = __esm(() => {
1254
- init_src();
1255
- TRAILING_SLASHES = /\/+$/;
1256
- PACKAGE_NAME = /^(?:@[\w~-][\w.~-]*\/)?[\w~-][\w.~-]*$/;
1257
- });
1258
-
1259
- // src/config.ts
1260
- import { homedir } from "os";
1261
- import { dirname, join } from "path";
1262
- import process8 from "process";
1263
- function expand2(value, env) {
1264
- let missing = false;
1265
- const expanded = value.replace(ENV_REFERENCE, (_, braced, bare) => {
1266
- const found = env[braced ?? bare ?? ""];
1267
- missing ||= found === undefined;
1268
- return found ?? "";
1269
- });
1270
- return missing ? undefined : expanded;
1271
- }
1272
- function literal(value) {
1273
- return expand2(value, {}) === value ? value : undefined;
1274
- }
1275
- function parseNpmrc(text, env) {
1276
- const config = { scopes: {}, tokens: {}, scopeTokens: {} };
1277
- for (const line of text.split(`
1278
- `)) {
1279
- const at = line.indexOf("=");
1280
- if (at === -1 || COMMENT.test(line))
1281
- continue;
1282
- const key = line.slice(0, at).trim();
1283
- const scope = SCOPE_REGISTRY.exec(key)?.[1];
1284
- const tokenFor = AUTH_TOKEN.exec(key)?.[1];
1285
- const raw = line.slice(at + 1).trim().replace(QUOTES, "$2");
1286
- const value = tokenFor === undefined ? literal(raw) : expand2(raw, env);
1287
- if (value === undefined || value === "")
1288
- continue;
1289
- if (scope !== undefined)
1290
- config.scopes[scope] = value;
1291
- else if (tokenFor !== undefined)
1292
- config.tokens[tokenFor] = value;
1293
- else if (key === "registry")
1294
- config.registry = value;
1295
- }
1296
- return config;
1297
- }
1298
- function bunEntry(entry, env) {
1299
- const url = typeof entry === "string" ? entry : entry.url;
1300
- const token = typeof entry === "string" || entry.token === undefined ? undefined : expand2(entry.token, env);
1301
- return { ...url === undefined || literal(url) === undefined ? {} : { url }, ...token === undefined ? {} : { token } };
1302
- }
1303
- function parseBunfig(text, env) {
1304
- const config = { scopes: {}, tokens: {}, scopeTokens: {} };
1305
- const install = Bun.TOML.parse(text).install;
1306
- if (install?.registry !== undefined) {
1307
- const { url, token } = bunEntry(install.registry, env);
1308
- if (url !== undefined)
1309
- config.registry = url;
1310
- if (url !== undefined && token !== undefined)
1311
- config.tokens[url.replace(PROTOCOL, "")] = token;
1312
- }
1313
- for (const [scope, entry] of Object.entries(install?.scopes ?? {})) {
1314
- const { url, token } = bunEntry(entry, env);
1315
- if (url !== undefined)
1316
- config.scopes[scope.startsWith("@") ? scope : `@${scope}`] = url;
1317
- if (token !== undefined)
1318
- config.scopeTokens[scope.startsWith("@") ? scope : `@${scope}`] = token;
1319
- }
1320
- return config;
1321
- }
1322
- function tokenFor(config, url) {
1323
- const address = `${url.replace(PROTOCOL, "").replace(TRAILING_SLASHES2, "")}/`;
1324
- const best = Object.keys(config.tokens).filter((key) => address.startsWith(`${key.replace(TRAILING_SLASHES2, "")}/`)).sort((a, b) => b.length - a.length)[0];
1325
- return best === undefined ? undefined : config.tokens[best];
1326
- }
1327
- function mayCarryToken(url) {
1328
- const { protocol, hostname } = new URL(url);
1329
- return protocol === "https:" || LOCAL_HOSTS.has(hostname);
1330
- }
1331
- function registryFor(name, configs) {
1332
- const scope = name.startsWith("@") ? name.slice(0, name.indexOf("/")) : undefined;
1333
- const scoped = scope === undefined ? undefined : configs.find((config) => config.scopes[scope] !== undefined);
1334
- const url = (scope === undefined ? undefined : scoped?.scopes[scope]) ?? configs.find((config) => config.registry !== undefined)?.registry;
1335
- if (url === undefined)
1336
- return { url: PUBLIC_REGISTRY };
1337
- const token = (scope === undefined ? undefined : scoped?.scopeTokens[scope]) ?? configs.map((other) => tokenFor(other, url)).find((found) => found !== undefined);
1338
- return { url, token: mayCarryToken(url) ? token : undefined };
1339
- }
1340
- function readable(path, parse) {
1341
- try {
1342
- return parse();
1343
- } catch (error) {
1344
- return unreachable(`${path} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
1345
- }
1346
- }
1347
- async function readIfThere(path) {
1348
- const file = Bun.file(path);
1349
- return await file.exists() ? file.text() : undefined;
1350
- }
1351
- async function loadConfigs(cwd = process8.cwd(), env = process8.env, home = homedir()) {
1352
- const directories = [];
1353
- for (let dir = cwd;!directories.includes(dir); dir = dirname(dir))
1354
- directories.push(dir);
1355
- if (!directories.includes(home))
1356
- directories.push(home);
1357
- const configs = [];
1358
- for (const dir of directories) {
1359
- const bunfig = await readIfThere(join(dir, "bunfig.toml"));
1360
- const npmrc = await readIfThere(join(dir, ".npmrc"));
1361
- if (bunfig !== undefined)
1362
- configs.push(readable(join(dir, "bunfig.toml"), () => parseBunfig(bunfig, env)));
1363
- if (npmrc !== undefined)
1364
- configs.push(parseNpmrc(npmrc, env));
1365
- }
1366
- return configs;
1367
- }
1368
- function isSeverity(value) {
1369
- return SEVERITIES.includes(value);
1370
- }
1371
- function parseSettings(text) {
1372
- const raw = Bun.JSONC.parse(text);
1373
- const settings = {};
1374
- if (Array.isArray(raw?.licenses) && raw.licenses.every((each) => typeof each === "string"))
1375
- settings.licenses = raw.licenses;
1376
- if (isSeverity(raw?.blockSeverity))
1377
- settings.blockSeverity = raw.blockSeverity;
1378
- return settings;
1379
- }
1380
- async function loadSettings(cwd = process8.cwd(), home = homedir()) {
1381
- const files = [];
1382
- for (let dir = cwd;!files.includes(join(dir, SETTINGS_FILE)); dir = dirname(dir))
1383
- files.push(join(dir, SETTINGS_FILE));
1384
- files.push(join(home, ".config", "pkg", "config.json"));
1385
- const settings = {};
1386
- for (const file of files) {
1387
- const text = await readIfThere(file);
1388
- if (text === undefined)
1389
- continue;
1390
- const found = readable(file, () => parseSettings(text));
1391
- settings.licenses ??= found.licenses;
1392
- settings.blockSeverity ??= found.blockSeverity;
1393
- }
1394
- return settings;
1395
- }
1396
- var ENV_REFERENCE, COMMENT, SCOPE_REGISTRY, AUTH_TOKEN, PROTOCOL, TRAILING_SLASHES2, LOCAL_HOSTS, QUOTES, SEVERITIES, DEFAULT_BLOCK_SEVERITY = "high", SETTINGS_FILE = ".pkgrc.json";
1397
- var init_config2 = __esm(() => {
1398
- init_registry();
1399
- ENV_REFERENCE = /\$\{(\w+)\}|\$(\w+)/g;
1400
- COMMENT = /^\s*[#;]/;
1401
- SCOPE_REGISTRY = /^(@[^:]+):registry$/;
1402
- AUTH_TOKEN = /^(\/\/.+?):_authToken$/;
1403
- PROTOCOL = /^\w+:/;
1404
- TRAILING_SLASHES2 = /\/+$/;
1405
- LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
1406
- QUOTES = /^(["'])(.*)\1$/;
1407
- SEVERITIES = ["low", "moderate", "high", "critical"];
1408
- });
1409
-
1410
- // src/semver.ts
1411
- function isRange(text) {
1412
- if (text.trim() === "")
1413
- return true;
1414
- return text.split(OR).every((alternative) => {
1415
- const words = alternative.trim().split(WHITESPACE);
1416
- return words.every((word, index) => word === "-" ? index > 0 && index < words.length - 1 : COMPARATOR.test(word));
1417
- });
1418
- }
1419
- function compare(version, word) {
1420
- const bound = BOUND.exec(word);
1421
- if (bound === null)
1422
- return;
1423
- const order = Bun.semver.order(version, bound[2] ?? "");
1424
- const operator = bound[1] ?? "=";
1425
- if (operator === "<")
1426
- return order < 0;
1427
- if (operator === "<=")
1428
- return order <= 0;
1429
- if (operator === ">")
1430
- return order > 0;
1431
- return operator === ">=" ? order >= 0 : order === 0;
1432
- }
1433
- function inRange(version, range) {
1434
- if (Bun.semver.satisfies(version, range))
1435
- return true;
1436
- if (!PRERELEASE.test(version))
1437
- return false;
1438
- return range.split(OR).some((alternative) => alternative.trim().split(WHITESPACE).every((word) => compare(version, word) ?? Bun.semver.satisfies(version, word)));
1439
- }
1440
- var OR, WHITESPACE, PRERELEASE, PART, COMPARATOR, BOUND, EXACT_VERSION;
1441
- var init_semver = __esm(() => {
1442
- OR = /\s*\|\|\s*/;
1443
- WHITESPACE = /\s+/;
1444
- PRERELEASE = /-/;
1445
- PART = String.raw`(?:\d+|[xX*])`;
1446
- COMPARATOR = new RegExp(String.raw`^(?:[<>]=?|=|\^|~>?)?v?${PART}(?:\.${PART}){0,2}(?:-[\w.-]+)?(?:\+[\w.-]+)?$`);
1447
- BOUND = /^(<=|>=|[<>=])?v?(\d+\.\d+\.\d+(?:-[\w.-]+)?)$/;
1448
- EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
1449
- });
1450
-
1451
- // src/advisories.ts
1452
- function severityRank(severity) {
1453
- return SEVERITIES.indexOf(severity);
1454
- }
1455
- function fixedVersion(vulnerable, version) {
1456
- const range = vulnerable.split("||").map((each) => each.trim()).find((each) => inRange(version, each));
1457
- return range === undefined ? undefined : UPPER_BOUND.exec(range)?.[1];
1458
- }
1459
- async function fetchAdvisories(versions, registry, fetcher = fetch) {
1460
- const url = `${registry.url.replace(TRAILING_SLASHES3, "")}${BULK_PATH}`;
1461
- const host = new URL(registry.url).host;
1462
- let response;
1541
+ var OR, WHITESPACE, PRERELEASE, PART, COMPARATOR, BOUND, EXACT_VERSION;
1542
+ var init_semver = __esm(() => {
1543
+ OR = /\s*\|\|\s*/;
1544
+ WHITESPACE = /\s+/;
1545
+ PRERELEASE = /-/;
1546
+ PART = String.raw`(?:\d+|[xX*])`;
1547
+ COMPARATOR = new RegExp(String.raw`^(?:[<>]=?|=|\^|~>?)?v?${PART}(?:\.${PART}){0,2}(?:-[\w.-]+)?(?:\+[\w.-]+)?$`);
1548
+ BOUND = /^(<=|>=|[<>=])?v?(\d+\.\d+\.\d+(?:-[\w.-]+)?)$/;
1549
+ EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
1550
+ });
1551
+
1552
+ // src/advisories.ts
1553
+ function fixedVersion(vulnerable, version) {
1554
+ const range = vulnerable.split("||").map((each) => each.trim()).find((each) => inRange(version, each));
1555
+ return range === undefined ? undefined : UPPER_BOUND.exec(range)?.[1];
1556
+ }
1557
+ async function fetchAdvisories(versions, registry, fetcher = fetch) {
1558
+ const url = `${registry.url.replace(TRAILING_SLASHES, "")}${BULK_PATH}`;
1559
+ const host = new URL(registry.url).host;
1560
+ let response;
1463
1561
  try {
1464
1562
  response = await fetcher(url, {
1465
1563
  method: "POST",
1466
1564
  headers: { accept: "application/json", "content-type": "application/json", ...registry.token === undefined ? {} : { authorization: `Bearer ${registry.token}` } },
1467
1565
  body: JSON.stringify(versions),
1468
- signal: AbortSignal.timeout(REQUEST_TIMEOUT2)
1566
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT)
1469
1567
  });
1470
1568
  } catch (error) {
1471
1569
  return { found: {}, unavailable: `cannot reach ${host} for advisories: ${error instanceof Error ? error.message : String(error)}` };
@@ -1499,13 +1597,13 @@ function advisoriesFor(advisories, version) {
1499
1597
  return (advisories ?? []).filter((each) => inRange(version, each.vulnerable)).map((each) => ({ ...each, fixed: fixedVersion(each.vulnerable, version) }));
1500
1598
  }
1501
1599
  function filterAdvisories(advisories, level, ignore = []) {
1502
- return advisories.filter((each) => severityRank(each.severity) >= severityRank(level) && !ignore.includes(each.ghsa ?? "") && !ignore.includes(String(each.id)));
1600
+ return advisories.filter((each) => severityRank(each.severity) >= severityRank(level) && !isIgnored(each, ignore));
1503
1601
  }
1504
- var BULK_PATH = "/-/npm/v1/security/advisories/bulk", TRAILING_SLASHES3, REQUEST_TIMEOUT2 = 1e4, GHSA, UPPER_BOUND, NO_ENDPOINT;
1602
+ var BULK_PATH = "/-/npm/v1/security/advisories/bulk", TRAILING_SLASHES, REQUEST_TIMEOUT = 1e4, GHSA, UPPER_BOUND, NO_ENDPOINT;
1505
1603
  var init_advisories = __esm(() => {
1506
- init_config2();
1604
+ init_src();
1507
1605
  init_semver();
1508
- TRAILING_SLASHES3 = /\/+$/;
1606
+ TRAILING_SLASHES = /\/+$/;
1509
1607
  GHSA = /GHSA-[\w-]+$/;
1510
1608
  UPPER_BOUND = /(?:^|\s)<([\w.+-]+)/;
1511
1609
  NO_ENDPOINT = new Set([404, 405, 501]);
@@ -2071,24 +2169,79 @@ var init_nearmiss = __esm(() => {
2071
2169
  SEPARATORS = /[-_.]/g;
2072
2170
  });
2073
2171
 
2074
- // src/analyze.ts
2075
- function parseSpec(text) {
2076
- const at = text.search(VERSION_SEPARATOR);
2077
- return at === -1 ? { name: text } : { name: text.slice(0, at), request: text.slice(at + 1) };
2172
+ // src/registry.ts
2173
+ function isPublic(registry) {
2174
+ return registry.url.replace(TRAILING_SLASHES2, "") === PUBLIC_REGISTRY;
2078
2175
  }
2079
- function isStable(version) {
2080
- return !version.includes("-");
2176
+ function unreachable(message) {
2177
+ return fail(`could not check: ${message}`, 2);
2081
2178
  }
2082
- function resolve(packument, request) {
2083
- const tags = packument["dist-tags"] ?? {};
2084
- const versions = Object.keys(packument.versions ?? {});
2085
- const wanted = request === undefined || request === "" ? "latest" : request;
2086
- if (tags[wanted] !== undefined)
2087
- return tags[wanted];
2088
- if (versions.includes(wanted))
2089
- return wanted;
2090
- if (!isRange(wanted))
2091
- return;
2179
+ function requestInit(token) {
2180
+ return { headers: { accept: "application/json", ...token === undefined ? {} : { authorization: `Bearer ${token}` } }, signal: AbortSignal.timeout(REQUEST_TIMEOUT2) };
2181
+ }
2182
+ async function get(fetcher, url, token) {
2183
+ try {
2184
+ return await fetcher(url, requestInit(token));
2185
+ } catch (error) {
2186
+ return unreachable(`cannot reach ${new URL(url).host}: ${error instanceof Error ? error.message : String(error)}`);
2187
+ }
2188
+ }
2189
+ function isValidName(name) {
2190
+ return PACKAGE_NAME.test(name) && name.length <= 214;
2191
+ }
2192
+ async function fetchPackument(name, registry, fetcher = fetch) {
2193
+ if (!isValidName(name))
2194
+ fail(`${name} is not a valid npm package name`);
2195
+ const response = await get(fetcher, `${registry.url.replace(TRAILING_SLASHES2, "")}/${encodeURIComponent(name).replace("%40", "@").replace("%2F", "%2f")}`, registry.token);
2196
+ if (response.status === 404)
2197
+ return;
2198
+ if (!response.ok)
2199
+ unreachable(`${new URL(registry.url).host} answered ${response.status} for ${name}`);
2200
+ let packument;
2201
+ try {
2202
+ packument = await response.json();
2203
+ } catch {
2204
+ return unreachable(`${new URL(registry.url).host} answered ${name} with something that is not JSON`);
2205
+ }
2206
+ if (packument?.name !== name)
2207
+ unreachable(`${new URL(registry.url).host} answered ${name} with the metadata of ${String(packument?.name)}`);
2208
+ return packument;
2209
+ }
2210
+ async function fetchDownloads(name, fetcher = fetch) {
2211
+ const response = await get(fetcher, `${DOWNLOADS_API}/${encodeURIComponent(name)}`);
2212
+ if (!response.ok)
2213
+ return;
2214
+ try {
2215
+ return (await response.json()).downloads;
2216
+ } catch {
2217
+ return;
2218
+ }
2219
+ }
2220
+ var PUBLIC_REGISTRY = "https://registry.npmjs.org", DOWNLOADS_API = "https://api.npmjs.org/downloads/point/last-week", TRAILING_SLASHES2, REQUEST_TIMEOUT2 = 1e4, PACKAGE_NAME;
2221
+ var init_registry = __esm(() => {
2222
+ init_src();
2223
+ TRAILING_SLASHES2 = /\/+$/;
2224
+ PACKAGE_NAME = /^(?:@[\w~-][\w.~-]*\/)?[\w~-][\w.~-]*$/;
2225
+ });
2226
+
2227
+ // src/analyze.ts
2228
+ function parseSpec(text) {
2229
+ const at = text.search(VERSION_SEPARATOR);
2230
+ return at === -1 ? { name: text } : { name: text.slice(0, at), request: text.slice(at + 1) };
2231
+ }
2232
+ function isStable(version) {
2233
+ return !version.includes("-");
2234
+ }
2235
+ function resolve(packument, request) {
2236
+ const tags = packument["dist-tags"] ?? {};
2237
+ const versions = Object.keys(packument.versions ?? {});
2238
+ const wanted = request === undefined || request === "" ? "latest" : request;
2239
+ if (tags[wanted] !== undefined)
2240
+ return tags[wanted];
2241
+ if (versions.includes(wanted))
2242
+ return wanted;
2243
+ if (!isRange(wanted))
2244
+ return;
2092
2245
  return versions.filter((version) => Bun.semver.satisfies(version, wanted)).sort(Bun.semver.order).at(-1);
2093
2246
  }
2094
2247
  function licenseOf(license) {
@@ -2190,6 +2343,310 @@ var init_analyze = __esm(() => {
2190
2343
  VERSION_SEPARATOR = /(?<=.)@/;
2191
2344
  });
2192
2345
 
2346
+ // src/config.ts
2347
+ import { homedir as homedir3 } from "os";
2348
+ import { dirname as dirname3, join as join4 } from "path";
2349
+ import process13 from "process";
2350
+ function expand2(value, env) {
2351
+ let missing = false;
2352
+ const expanded = value.replace(ENV_REFERENCE, (_, braced, bare) => {
2353
+ const found = env[braced ?? bare ?? ""];
2354
+ missing ||= found === undefined;
2355
+ return found ?? "";
2356
+ });
2357
+ return missing ? undefined : expanded;
2358
+ }
2359
+ function literal(value) {
2360
+ return expand2(value, {}) === value ? value : undefined;
2361
+ }
2362
+ function parseNpmrc(text, env) {
2363
+ const config = { scopes: {}, tokens: {}, scopeTokens: {} };
2364
+ for (const line of text.split(`
2365
+ `)) {
2366
+ const at = line.indexOf("=");
2367
+ if (at === -1 || COMMENT.test(line))
2368
+ continue;
2369
+ const key = line.slice(0, at).trim();
2370
+ const scope = SCOPE_REGISTRY.exec(key)?.[1];
2371
+ const tokenFor = AUTH_TOKEN.exec(key)?.[1];
2372
+ const raw = line.slice(at + 1).trim().replace(QUOTES, "$2");
2373
+ const value = tokenFor === undefined ? literal(raw) : expand2(raw, env);
2374
+ if (value === undefined || value === "")
2375
+ continue;
2376
+ if (scope !== undefined)
2377
+ config.scopes[scope] = value;
2378
+ else if (tokenFor !== undefined)
2379
+ config.tokens[tokenFor] = value;
2380
+ else if (key === "registry")
2381
+ config.registry = value;
2382
+ }
2383
+ return config;
2384
+ }
2385
+ function bunEntry(entry, env) {
2386
+ const url = typeof entry === "string" ? entry : entry.url;
2387
+ const token = typeof entry === "string" || entry.token === undefined ? undefined : expand2(entry.token, env);
2388
+ return { ...url === undefined || literal(url) === undefined ? {} : { url }, ...token === undefined ? {} : { token } };
2389
+ }
2390
+ function parseBunfig(text, env) {
2391
+ const config = { scopes: {}, tokens: {}, scopeTokens: {} };
2392
+ const install = Bun.TOML.parse(text).install;
2393
+ if (install?.registry !== undefined) {
2394
+ const { url, token } = bunEntry(install.registry, env);
2395
+ if (url !== undefined)
2396
+ config.registry = url;
2397
+ if (url !== undefined && token !== undefined)
2398
+ config.tokens[url.replace(PROTOCOL, "")] = token;
2399
+ }
2400
+ for (const [scope, entry] of Object.entries(install?.scopes ?? {})) {
2401
+ const { url, token } = bunEntry(entry, env);
2402
+ if (url !== undefined)
2403
+ config.scopes[scope.startsWith("@") ? scope : `@${scope}`] = url;
2404
+ if (token !== undefined)
2405
+ config.scopeTokens[scope.startsWith("@") ? scope : `@${scope}`] = token;
2406
+ }
2407
+ return config;
2408
+ }
2409
+ function tokenFor(config, url) {
2410
+ const address = `${url.replace(PROTOCOL, "").replace(TRAILING_SLASHES3, "")}/`;
2411
+ const best = Object.keys(config.tokens).filter((key) => address.startsWith(`${key.replace(TRAILING_SLASHES3, "")}/`)).sort((a, b) => b.length - a.length)[0];
2412
+ return best === undefined ? undefined : config.tokens[best];
2413
+ }
2414
+ function mayCarryToken(url) {
2415
+ const { protocol, hostname } = new URL(url);
2416
+ return protocol === "https:" || LOCAL_HOSTS.has(hostname);
2417
+ }
2418
+ function registryFor(name, configs) {
2419
+ const scope = name.startsWith("@") ? name.slice(0, name.indexOf("/")) : undefined;
2420
+ const scoped = scope === undefined ? undefined : configs.find((config) => config.scopes[scope] !== undefined);
2421
+ const url = (scope === undefined ? undefined : scoped?.scopes[scope]) ?? configs.find((config) => config.registry !== undefined)?.registry;
2422
+ if (url === undefined)
2423
+ return { url: PUBLIC_REGISTRY };
2424
+ const token = (scope === undefined ? undefined : scoped?.scopeTokens[scope]) ?? configs.map((other) => tokenFor(other, url)).find((found) => found !== undefined);
2425
+ return { url, token: mayCarryToken(url) ? token : undefined };
2426
+ }
2427
+ function readable(path, parse) {
2428
+ try {
2429
+ return parse();
2430
+ } catch (error) {
2431
+ return unreachable(`${path} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
2432
+ }
2433
+ }
2434
+ async function readIfThere(path) {
2435
+ const file = Bun.file(path);
2436
+ return await file.exists() ? file.text() : undefined;
2437
+ }
2438
+ async function loadConfigs(cwd = process13.cwd(), env = process13.env, home = homedir3()) {
2439
+ const directories = [];
2440
+ for (let dir = cwd;!directories.includes(dir); dir = dirname3(dir))
2441
+ directories.push(dir);
2442
+ if (!directories.includes(home))
2443
+ directories.push(home);
2444
+ const configs = [];
2445
+ for (const dir of directories) {
2446
+ const bunfig = await readIfThere(join4(dir, "bunfig.toml"));
2447
+ const npmrc = await readIfThere(join4(dir, ".npmrc"));
2448
+ if (bunfig !== undefined)
2449
+ configs.push(readable(join4(dir, "bunfig.toml"), () => parseBunfig(bunfig, env)));
2450
+ if (npmrc !== undefined)
2451
+ configs.push(parseNpmrc(npmrc, env));
2452
+ }
2453
+ return configs;
2454
+ }
2455
+ function isSeverity(value) {
2456
+ return SEVERITIES.includes(value);
2457
+ }
2458
+ function parseSettings(text) {
2459
+ const raw = Bun.JSONC.parse(text);
2460
+ const settings = {};
2461
+ if (Array.isArray(raw?.licenses) && raw.licenses.every((each) => typeof each === "string"))
2462
+ settings.licenses = raw.licenses;
2463
+ if (isSeverity(raw?.blockSeverity))
2464
+ settings.blockSeverity = raw.blockSeverity;
2465
+ return settings;
2466
+ }
2467
+ async function loadSettings(cwd = process13.cwd(), home) {
2468
+ const files = [];
2469
+ for (let dir = cwd;!files.includes(join4(dir, SETTINGS_FILE)); dir = dirname3(dir))
2470
+ files.push(join4(dir, SETTINGS_FILE));
2471
+ files.push(userConfigReadPath("pkg", [["pkg", "config.json"]], { home }));
2472
+ const settings = {};
2473
+ for (const file of files) {
2474
+ const text = await readIfThere(file);
2475
+ if (text === undefined)
2476
+ continue;
2477
+ const found = readable(file, () => parseSettings(text));
2478
+ settings.licenses ??= found.licenses;
2479
+ settings.blockSeverity ??= found.blockSeverity;
2480
+ }
2481
+ return settings;
2482
+ }
2483
+ var ENV_REFERENCE, COMMENT, SCOPE_REGISTRY, AUTH_TOKEN, PROTOCOL, TRAILING_SLASHES3, LOCAL_HOSTS, QUOTES, DEFAULT_BLOCK_SEVERITY = "high", SETTINGS_FILE = ".pkgrc.json";
2484
+ var init_config2 = __esm(() => {
2485
+ init_src();
2486
+ init_registry();
2487
+ ENV_REFERENCE = /\$\{(\w+)\}|\$(\w+)/g;
2488
+ COMMENT = /^\s*[#;]/;
2489
+ SCOPE_REGISTRY = /^(@[^:]+):registry$/;
2490
+ AUTH_TOKEN = /^(\/\/.+?):_authToken$/;
2491
+ PROTOCOL = /^\w+:/;
2492
+ TRAILING_SLASHES3 = /\/+$/;
2493
+ LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
2494
+ QUOTES = /^(["'])(.*)\1$/;
2495
+ });
2496
+
2497
+ // ../kit/src/shell.ts
2498
+ function wrapper(short = "", long = [], operands = 0, runsString = false) {
2499
+ return { short, long, operands, runsString };
2500
+ }
2501
+ var SEPARATORS2, PLAIN_BREAKERS, GLOB_CHARS, COMMENT_PREFIXES, DOUBLE_QUOTED_ESCAPES, KEYWORDS, SHELLS, SCANNED_PROGRAMS, COMMAND_STRING_PROGRAMS, POWERSHELL, SHELL_VALUE_OPTIONS, FISH_VALUE_OPTIONS, PRINTERS, FIND_EXEC, WRAPPERS;
2502
+ var init_shell = __esm(() => {
2503
+ SEPARATORS2 = new Set([`
2504
+ `, ";", "|"]);
2505
+ PLAIN_BREAKERS = new Set(["<", ">", "(", ")", "&", "|"]);
2506
+ GLOB_CHARS = new Set(["*", "?", "["]);
2507
+ COMMENT_PREFIXES = new Set([";", "&", "|", "(", ")"]);
2508
+ DOUBLE_QUOTED_ESCAPES = new Set(["$", "`", '"', "\\"]);
2509
+ KEYWORDS = new Set(["then", "do", "else", "if", "elif", "while", "until", "!", "{", "}"]);
2510
+ SHELLS = new Set(["sh", "bash", "zsh", "dash", "ash", "ksh", "fish", "csh", "tcsh", "nu", "xonsh", "elvish", "pwsh", "powershell"]);
2511
+ SCANNED_PROGRAMS = new Set(["su", "runuser", "flock"]);
2512
+ COMMAND_STRING_PROGRAMS = new Set([...SHELLS, ...SCANNED_PROGRAMS]);
2513
+ POWERSHELL = new Set(["pwsh", "powershell"]);
2514
+ SHELL_VALUE_OPTIONS = new Set(["-o", "-O", "--rcfile", "--init-file"]);
2515
+ FISH_VALUE_OPTIONS = new Set(["-C", "--init-command"]);
2516
+ PRINTERS = new Set(["echo", "printf"]);
2517
+ FIND_EXEC = new Set(["-exec", "-execdir", "-ok", "-okdir"]);
2518
+ WRAPPERS = new Map([
2519
+ ["command", wrapper()],
2520
+ ["builtin", wrapper()],
2521
+ ["nohup", wrapper()],
2522
+ ["setsid", wrapper()],
2523
+ ["busybox", wrapper()],
2524
+ ["exec", wrapper("a")],
2525
+ ["time", wrapper("fo", ["--format", "--output"])],
2526
+ ["env", wrapper("uCS", ["--unset", "--chdir", "--split-string", "--argv0"])],
2527
+ ["sudo", wrapper("ughpCTrtUDR", ["--user", "--group", "--host", "--prompt", "--close-from", "--command-timeout", "--role", "--type", "--other-user", "--chdir", "--chroot"])],
2528
+ ["doas", wrapper("uC")],
2529
+ ["timeout", wrapper("sk", ["--signal", "--kill-after"], 1)],
2530
+ ["nice", wrapper("n", ["--adjustment"])],
2531
+ ["ionice", wrapper("cnpPu", ["--class", "--classdata", "--pid", "--pgid", "--uid"])],
2532
+ ["stdbuf", wrapper("ioe", ["--input", "--output", "--error"])],
2533
+ ["chroot", wrapper("", ["--userspec", "--groups"], 1)],
2534
+ ["watch", wrapper("n", ["--interval"], 0, true)],
2535
+ ["flock", wrapper("wE", ["--timeout", "--conflict-exit-code"], 1)],
2536
+ ["parallel", wrapper("jnNSaIPlLdTsC", ["--jobs", "--max-args", "--max-replace-args", "--sshlogin", "--arg-file", "--delimiter", "--colsep"], 0, true)],
2537
+ ["unbuffer", wrapper()],
2538
+ ["systemd-run", wrapper("puEMH", ["--property", "--unit", "--description", "--slice", "--setenv", "--machine", "--host", "--uid", "--gid", "--working-directory", "--nice", "--service-type"])],
2539
+ ["xargs", wrapper("IndsEaPL", ["--max-args", "--max-procs", "--max-chars", "--delimiter", "--arg-file", "--eof", "--max-lines", "--process-slot-var"])]
2540
+ ]);
2541
+ });
2542
+
2543
+ // src/pin.ts
2544
+ function actions(byAction) {
2545
+ return new Map(Object.entries(byAction).flatMap(([action, names]) => names.map((name) => [name, action])));
2546
+ }
2547
+ var LOCKFILES, ACTIONS, OPTION_FLAGS, DIR_FLAGS, VALUE_FLAGS;
2548
+ var init_pin = __esm(() => {
2549
+ init_shell();
2550
+ LOCKFILES = [
2551
+ ["bun.lock", "bun"],
2552
+ ["bun.lockb", "bun"],
2553
+ ["pnpm-lock.yaml", "pnpm"],
2554
+ ["yarn.lock", "yarn"],
2555
+ ["package-lock.json", "npm"]
2556
+ ];
2557
+ ACTIONS = {
2558
+ bun: actions({ install: ["install", "i"], add: ["add", "a"], remove: ["remove", "rm"], update: ["update"] }),
2559
+ pnpm: actions({ install: ["install", "i"], add: ["add"], remove: ["remove", "rm"], update: ["update", "up"] }),
2560
+ yarn: new Map([...actions({ install: ["", "install"], add: ["add"], remove: ["remove"], update: ["upgrade"] }), ["upgrade-interactive", null], ["import", null]]),
2561
+ npm: actions({
2562
+ install: ["install", "i", "in", "ins", "inst", "insta", "instal", "isnt", "isnta", "isntal", "isntall"],
2563
+ add: ["add"],
2564
+ frozen: ["ci", "clean-install", "ic", "install-clean", "isntall-clean"],
2565
+ remove: ["uninstall", "unlink", "remove", "rm", "r", "un"],
2566
+ update: ["update", "u", "up", "upgrade", "udpate"]
2567
+ })
2568
+ };
2569
+ OPTION_FLAGS = new Map([
2570
+ ["-D", "dev"],
2571
+ ["--save-dev", "dev"],
2572
+ ["--dev", "dev"],
2573
+ ["-E", "exact"],
2574
+ ["--save-exact", "exact"],
2575
+ ["--exact", "exact"]
2576
+ ]);
2577
+ DIR_FLAGS = new Set(["--cwd", "-C", "--dir", "--prefix"]);
2578
+ VALUE_FLAGS = new Set([...DIR_FLAGS, "--filter", "-F"]);
2579
+ });
2580
+
2581
+ // src/consumers.ts
2582
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
2583
+ import { dirname as dirname4, join as join5, relative } from "path";
2584
+ function packageFiles(dir, depth, found) {
2585
+ let entries;
2586
+ try {
2587
+ entries = readdirSync(dir, { withFileTypes: true });
2588
+ } catch {
2589
+ return found;
2590
+ }
2591
+ for (const entry of entries) {
2592
+ if (entry.isFile() && entry.name === "package.json")
2593
+ found.push(join5(dir, entry.name));
2594
+ else if (entry.isDirectory() && depth < MAX_DEPTH && !entry.name.startsWith(".") && !SKIPPED.has(entry.name))
2595
+ packageFiles(join5(dir, entry.name), depth + 1, found);
2596
+ }
2597
+ return found;
2598
+ }
2599
+ function managerFor(dir, root) {
2600
+ for (let at = dir;; at = dirname4(at)) {
2601
+ const lockfile = LOCKFILES.find(([file]) => existsSync2(join5(at, file)));
2602
+ if (lockfile !== undefined)
2603
+ return lockfile[1];
2604
+ if (at === root || at === dirname4(at))
2605
+ return "bun";
2606
+ }
2607
+ }
2608
+ function findConsumers(name, root) {
2609
+ const consumers = [];
2610
+ for (const path of packageFiles(root, 0, []).sort()) {
2611
+ let manifest;
2612
+ try {
2613
+ manifest = JSON.parse(readFileSync2(path, "utf8"));
2614
+ } catch {
2615
+ continue;
2616
+ }
2617
+ const kind = KINDS.find((each) => typeof manifest[each]?.[name] === "string");
2618
+ const spec = kind === undefined ? undefined : manifest[kind]?.[name];
2619
+ if (kind !== undefined && spec !== undefined)
2620
+ consumers.push({ dir: relative(root, dirname4(path)) || ".", path, kind, spec, manager: managerFor(dirname4(path), root) });
2621
+ }
2622
+ return consumers;
2623
+ }
2624
+ function isLocal(consumer) {
2625
+ return LOCAL_SPEC.test(consumer.spec);
2626
+ }
2627
+ function addCommand(consumer, name, version) {
2628
+ return ["bun", "add", ...KIND_FLAGS[consumer.kind], "--exact", "--no-cache", `${name}@${version}`];
2629
+ }
2630
+ function registryDependencies(text) {
2631
+ let manifest;
2632
+ try {
2633
+ manifest = JSON.parse(text);
2634
+ } catch {
2635
+ return fail("the package.json is not valid JSON");
2636
+ }
2637
+ return REGISTRY_KINDS.flatMap((kind) => Object.entries(manifest?.[kind] ?? {}).flatMap(([name, spec]) => typeof spec === "string" && !LOCAL_SPEC.test(spec) ? [{ name, kind, spec }] : []));
2638
+ }
2639
+ var KINDS, SKIPPED, MAX_DEPTH = 5, LOCAL_SPEC, KIND_FLAGS, REGISTRY_KINDS;
2640
+ var init_consumers = __esm(() => {
2641
+ init_src();
2642
+ init_pin();
2643
+ KINDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
2644
+ SKIPPED = new Set(["node_modules", "dist", "build", "vendor", "target", "coverage"]);
2645
+ LOCAL_SPEC = /[:/]/;
2646
+ KIND_FLAGS = { dependencies: [], devDependencies: ["--dev"], optionalDependencies: ["--optional"], peerDependencies: ["--peer"] };
2647
+ REGISTRY_KINDS = KINDS.filter((kind) => kind !== "peerDependencies");
2648
+ });
2649
+
2193
2650
  // src/commands/check.ts
2194
2651
  var exports_check = {};
2195
2652
  __export(exports_check, {
@@ -2239,13 +2696,20 @@ function render(report, now = Date.now()) {
2239
2696
  ].join(`
2240
2697
  `);
2241
2698
  }
2699
+ async function wanted(positionals, from) {
2700
+ const names = positionals.filter((each) => each !== "-");
2701
+ const fromFile = from === undefined ? [] : registryDependencies(await readInput(from)).map(({ name, spec }) => `${name}@${spec}`);
2702
+ const fromStdin = positionals.includes("-") || names.length === 0 && fromFile.length === 0 && isStdinPiped() ? (await readInput("-")).split(WORDS).filter((each) => each !== "") : [];
2703
+ return [...new Set([...names, ...fromFile, ...fromStdin])];
2704
+ }
2242
2705
  async function check(args) {
2243
- const parsed = parse(args, usage, options);
2244
- const { values, positionals } = parsed;
2706
+ const parsed = parse(args, usage2, options, { exits: EXITS });
2707
+ const { values } = parsed;
2708
+ const positionals = await wanted(parsed.positionals, values.from);
2245
2709
  if (positionals.length === 0)
2246
- fail(`a package name is needed
2710
+ fail(values.from === undefined ? `a package name is needed
2247
2711
 
2248
- ${usage}`);
2712
+ ${usage2}` : `${values.from} has no registry dependency to check`);
2249
2713
  const context = { configs: await loadConfigs(), settings: await loadSettings(), minAgeDays: wholeNumber(values["min-age"], "min-age", DEFAULT_MIN_AGE_DAYS, 0) };
2250
2714
  const reports = await Promise.all(positionals.map(async (text) => checkPackage(text, context)));
2251
2715
  if (values.json === true)
@@ -2256,23 +2720,27 @@ ${usage}`);
2256
2720
  `));
2257
2721
  return reports.some((report) => report.blocked) ? 1 : 0;
2258
2722
  }
2259
- var usage = `pkg [check] <name>[@version]...
2723
+ var usage2 = `pkg [check] <name>[@version]... | --from <package.json> | -
2260
2724
 
2261
2725
  Checks npm packages before they are added: whether the name exists, the latest and the newest version at least two weeks old, weekly downloads, licence, repository, install scripts and a near miss of a popular name.
2262
2726
 
2263
- A \`.pkgrc.json\` (or ~/.config/pkg/config.json) with \`licenses\` (SPDX ids) refuses a licence outside the list or a missing one, and \`blockSeverity\` (low, moderate, high, critical; high when unset) refuses a version with an advisory at that level or above.
2727
+ Names come from the arguments, from the dependencies of a package.json with --from (each checked as \`name@range\`, so it resolves what an install would; dev and optional ones included, peer ranges and local or remote sources left out), or from stdin, separated by spaces or lines, when \`-\` is given or nothing else is and stdin is piped.
2264
2728
 
2265
- Exits 0 when every package passes, 1 when one is blocked (missing, the version asked for is under the minimum age or has no date, its licence is outside the allowlist or an advisory reaches the threshold), 2 when it could not check (the registry unreachable, slow or answering badly, a broken .pkgrc.json, bunfig.toml or .npmrc) or on a usage error.`, options;
2729
+ A \`.pkgrc.json\` (or ~/.config/kaynooo-cli/pkg.json) with \`licenses\` (SPDX ids) refuses a licence outside the list or a missing one, and \`blockSeverity\` (low, moderate, high, critical; high when unset) refuses a version with an advisory at that level or above.`, EXITS, options, WORDS;
2266
2730
  var init_check = __esm(() => {
2267
2731
  init_src();
2268
2732
  init_advisories();
2269
2733
  init_analyze();
2270
2734
  init_config2();
2735
+ init_consumers();
2271
2736
  init_registry();
2737
+ EXITS = [[0, "every package passes"], [1, "one is blocked: missing, the version asked for is under the minimum age or has no date, its licence is outside the allowlist or an advisory reaches the threshold"], [2, "it could not check (the registry unreachable, slow or answering badly, a broken .pkgrc.json, bunfig.toml or .npmrc) or a usage error"]];
2272
2738
  options = {
2273
2739
  json: jsonOption(),
2274
- "min-age": { type: "string", description: `days a version must be old (${DEFAULT_MIN_AGE_DAYS})` }
2740
+ "min-age": { type: "string", description: `days a version must be old (${DEFAULT_MIN_AGE_DAYS})` },
2741
+ from: { type: "string", description: "check the dependencies of this package.json" }
2275
2742
  };
2743
+ WORDS = /\s+/;
2276
2744
  });
2277
2745
 
2278
2746
  // src/lockfile.ts
@@ -2298,6 +2766,42 @@ function parseBunLock(text) {
2298
2766
  }
2299
2767
  return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name) || Bun.semver.order(a.version, b.version));
2300
2768
  }
2769
+ function parentKey(key) {
2770
+ const parts = key.split("/");
2771
+ const drop = parts.length > 1 && parts.at(-2)?.startsWith("@") ? 2 : 1;
2772
+ return parts.length > drop ? parts.slice(0, -drop).join("/") : undefined;
2773
+ }
2774
+ function productionPackages(text) {
2775
+ const lock = Bun.JSONC.parse(text);
2776
+ const packages = lock?.packages ?? {};
2777
+ const reached = new Set;
2778
+ const visited = new Set;
2779
+ const pending = [];
2780
+ for (const workspace of Object.values(lock?.workspaces ?? {})) {
2781
+ for (const name of Object.keys({ ...workspace.dependencies, ...workspace.optionalDependencies }))
2782
+ pending.push({ name, from: undefined });
2783
+ }
2784
+ while (pending.length > 0) {
2785
+ const { name, from } = pending.pop();
2786
+ let scope = from;
2787
+ let key = scope === undefined ? name : `${scope}/${name}`;
2788
+ while (!(key in packages) && scope !== undefined) {
2789
+ scope = parentKey(scope);
2790
+ key = scope === undefined ? name : `${scope}/${name}`;
2791
+ }
2792
+ const entry = packages[key];
2793
+ if (entry === undefined || visited.has(key))
2794
+ continue;
2795
+ visited.add(key);
2796
+ const locked = typeof entry[0] === "string" ? split(entry[0]) : undefined;
2797
+ if (locked !== undefined)
2798
+ reached.add(`${locked.name}@${locked.version}`);
2799
+ const info = entry[2] ?? {};
2800
+ for (const dependency of Object.keys({ ...info.dependencies, ...info.optionalDependencies }))
2801
+ pending.push({ name: dependency, from: key });
2802
+ }
2803
+ return reached;
2804
+ }
2301
2805
  var NOT_REGISTRY, VERSION_SEPARATOR2, NPM_ALIAS = "npm:";
2302
2806
  var init_lockfile = __esm(() => {
2303
2807
  NOT_REGISTRY = /^(?:workspace|link|file|github|git|http|https):|^[\w.-]+\/[\w.-]+$/;
@@ -2309,8 +2813,8 @@ var exports_audit = {};
2309
2813
  __export(exports_audit, {
2310
2814
  audit: () => audit
2311
2815
  });
2312
- import { join as join2, resolve as resolve2 } from "path";
2313
- import process9 from "process";
2816
+ import { join as join6, resolve as resolve2 } from "path";
2817
+ import process14 from "process";
2314
2818
  function hasFinding(audited) {
2315
2819
  return audited.blocked || audited.warnings.length > 0 || audited.advisories.length > 0;
2316
2820
  }
@@ -2365,15 +2869,17 @@ function renderAudit(findings, total) {
2365
2869
  `);
2366
2870
  }
2367
2871
  async function audit(args) {
2368
- const { values, positionals: [asked] } = parse(args, usage2, options2, { positionals: ["[dir]"] });
2369
- const level = values["audit-level"] ?? "low";
2872
+ const { values, positionals: [asked] } = parse(args, usage3, options2, { positionals: ["[dir]"], exits: EXITS2 });
2873
+ const level = values.level ?? "moderate";
2370
2874
  if (!isSeverity(level))
2371
- fail(`--audit-level takes ${SEVERITIES.join(", ")}, not ${level}`);
2372
- const dir = resolve2(asked ?? process9.cwd());
2373
- const lock = Bun.file(join2(dir, "bun.lock"));
2875
+ fail(`--level takes ${SEVERITIES.join(", ")}, not ${level}`);
2876
+ const dir = resolve2(asked ?? process14.cwd());
2877
+ const lock = Bun.file(join6(dir, "bun.lock"));
2374
2878
  if (!await lock.exists())
2375
2879
  fail(`no bun.lock in ${dir}`);
2376
- const packages = parseBunLock(await lock.text());
2880
+ const text = await lock.text();
2881
+ const production = values.prod === true ? productionPackages(text) : undefined;
2882
+ const packages = parseBunLock(text).filter(({ name, version }) => production?.has(`${name}@${version}`) ?? true);
2377
2883
  const context = {
2378
2884
  configs: await loadConfigs(dir),
2379
2885
  settings: await loadSettings(dir),
@@ -2393,12 +2899,11 @@ async function audit(args) {
2393
2899
  return 1;
2394
2900
  return unavailable.length > 0 ? 2 : 0;
2395
2901
  }
2396
- var usage2 = `pkg audit [dir]
2902
+ var usage3 = `pkg audit [dir]
2397
2903
 
2398
2904
  Reads the bun.lock in the folder (the current one by default) and runs on every registry package it pins the checks \`pkg check\` runs before an add: age, install scripts, a deprecated version, a near miss of a popular name, and the licence allowlist of the pkg config. It also lists the known advisories of each version, with the GHSA, the severity and the fixed version, from the registry's bulk advisory endpoint, the one \`bun audit\` reads.
2399
2905
  Findings are grouped per package, each registry package looked up once per run.
2400
-
2401
- Exits 0 when nothing blocks, 1 when a package is missing, too fresh or outside the licence allowlist, or an advisory reaches --audit-level, 2 when the registry could not be reached or answered badly (a warning says which, and nothing is reported as clean), on a usage error or without a bun.lock.`, options2, BATCH = 8;
2906
+ Reach for it to vet one repository in depth; \`audit\` sweeps many repositories for advisories only, and shares --level, --prod and --ignore.`, EXITS2, options2, BATCH = 8;
2402
2907
  var init_audit = __esm(() => {
2403
2908
  init_src();
2404
2909
  init_advisories();
@@ -2406,146 +2911,95 @@ var init_audit = __esm(() => {
2406
2911
  init_config2();
2407
2912
  init_lockfile();
2408
2913
  init_registry();
2914
+ EXITS2 = [[0, "nothing blocks"], [1, "a package is missing, too fresh or outside the licence allowlist, or an advisory reaches --level"], [2, "the registry could not be reached or answered badly (a warning says which, and nothing is reported as clean), a usage error or no bun.lock"]];
2409
2915
  options2 = {
2410
2916
  json: jsonOption("[{name, version, license, blocked, warnings, advisories}] for the packages with a finding"),
2411
2917
  "min-age": { type: "string", description: `days a version must be old (${DEFAULT_MIN_AGE_DAYS})` },
2412
- "audit-level": { type: "string", description: "lowest advisory severity listed and failing: low, moderate, high, critical (low)" },
2918
+ level: { type: "string", description: "lowest advisory severity listed and failing: low, moderate, high, critical (moderate)" },
2919
+ prod: { type: "boolean", description: "only the packages a production dependency or an optional one of a workspace reaches" },
2413
2920
  ignore: { type: "string", multiple: true, description: "an advisory skipped by GHSA or numeric id, repeatable" }
2414
2921
  };
2415
2922
  });
2416
2923
 
2417
- // ../kit/src/shell.ts
2418
- function wrapper(short = "", long = [], operands = 0) {
2419
- return { short, long, operands };
2420
- }
2421
- var SEPARATORS2, PLAIN_BREAKERS, GLOB_CHARS, COMMENT_PREFIXES, DOUBLE_QUOTED_ESCAPES, KEYWORDS, SHELLS, COMMAND_STRING_PROGRAMS, SHELL_VALUE_OPTIONS, PRINTERS, FIND_EXEC, WRAPPERS;
2422
- var init_shell = __esm(() => {
2423
- SEPARATORS2 = new Set([`
2424
- `, ";", "|"]);
2425
- PLAIN_BREAKERS = new Set(["<", ">", "(", ")", "&", "|"]);
2426
- GLOB_CHARS = new Set(["*", "?", "["]);
2427
- COMMENT_PREFIXES = new Set([";", "&", "|", "(", ")"]);
2428
- DOUBLE_QUOTED_ESCAPES = new Set(["$", "`", '"', "\\"]);
2429
- KEYWORDS = new Set(["then", "do", "else", "if", "elif", "while", "until", "!", "{", "}"]);
2430
- SHELLS = new Set(["sh", "bash", "zsh", "dash", "ash", "ksh"]);
2431
- COMMAND_STRING_PROGRAMS = new Set([...SHELLS, "su", "runuser"]);
2432
- SHELL_VALUE_OPTIONS = new Set(["-o", "-O", "--rcfile", "--init-file"]);
2433
- PRINTERS = new Set(["echo", "printf"]);
2434
- FIND_EXEC = new Set(["-exec", "-execdir", "-ok", "-okdir"]);
2435
- WRAPPERS = new Map([
2436
- ["command", wrapper()],
2437
- ["builtin", wrapper()],
2438
- ["nohup", wrapper()],
2439
- ["setsid", wrapper()],
2440
- ["busybox", wrapper()],
2441
- ["exec", wrapper("a")],
2442
- ["time", wrapper("fo", ["--format", "--output"])],
2443
- ["env", wrapper("uCS", ["--unset", "--chdir", "--split-string", "--argv0"])],
2444
- ["sudo", wrapper("ughpCTrtUDR", ["--user", "--group", "--host", "--prompt", "--close-from", "--command-timeout", "--role", "--type", "--other-user", "--chdir", "--chroot"])],
2445
- ["doas", wrapper("uC")],
2446
- ["timeout", wrapper("sk", ["--signal", "--kill-after"], 1)],
2447
- ["nice", wrapper("n", ["--adjustment"])],
2448
- ["ionice", wrapper("cnpPu", ["--class", "--classdata", "--pid", "--pgid", "--uid"])],
2449
- ["stdbuf", wrapper("ioe", ["--input", "--output", "--error"])],
2450
- ["chroot", wrapper("", ["--userspec", "--groups"], 1)],
2451
- ["xargs", wrapper("IndsEaPL", ["--max-args", "--max-procs", "--max-chars", "--delimiter", "--arg-file", "--eof", "--max-lines", "--process-slot-var"])]
2452
- ]);
2924
+ // src/commands/outdated.ts
2925
+ var exports_outdated = {};
2926
+ __export(exports_outdated, {
2927
+ outdatedCommand: () => outdatedCommand
2453
2928
  });
2454
-
2455
- // src/pin.ts
2456
- function actions(byAction) {
2457
- return new Map(Object.entries(byAction).flatMap(([action, names]) => names.map((name) => [name, action])));
2929
+ import { existsSync as existsSync3 } from "fs";
2930
+ import { join as join7, resolve as resolve3 } from "path";
2931
+ function currentOf(name, spec, locked) {
2932
+ const pinned = locked.filter((each) => each.name === name && Bun.semver.satisfies(each.version, spec)).map((each) => each.version).sort(Bun.semver.order).at(-1);
2933
+ const written = FIRST_VERSION.exec(spec)?.[0];
2934
+ return pinned ?? (written === undefined ? undefined : [...written.split("."), "0", "0"].slice(0, 3).join("."));
2935
+ }
2936
+ function changeOf(current, next) {
2937
+ const [from, to] = [current, next].map((version) => version.split(DOT).map(Number));
2938
+ if (from?.[0] !== to?.[0])
2939
+ return "major";
2940
+ return from?.[1] === to?.[1] ? "patch" : "minor";
2941
+ }
2942
+ async function outdated(manifest, context) {
2943
+ const configs = await loadConfigs();
2944
+ const dependencies = registryDependencies(manifest);
2945
+ const rows = [];
2946
+ for (let start = 0;start < dependencies.length; start += BATCH2) {
2947
+ const looked = await Promise.all(dependencies.slice(start, start + BATCH2).map(async ({ name, kind, spec }) => {
2948
+ const registry = registryFor(name, configs);
2949
+ const packument = await fetchPackument(name, registry, context.fetcher);
2950
+ if (packument === undefined) {
2951
+ warn(`${name} is not on ${new URL(registry.url).host}`);
2952
+ return;
2953
+ }
2954
+ const { safe, latest } = analyze({ spec: { name }, packument, registry: new URL(registry.url).host, now: context.now, minAgeDays: context.minAgeDays });
2955
+ const current = currentOf(name, spec, context.locked);
2956
+ if (safe === undefined || current === undefined || Bun.semver.order(safe.version, current) <= 0)
2957
+ return;
2958
+ const row = { name, kind, spec, current, safe: safe.version, change: changeOf(current, safe.version), published: safe.published };
2959
+ return latest !== undefined && latest.version !== safe.version && Bun.semver.order(latest.version, safe.version) > 0 ? { ...row, latest: latest.version } : row;
2960
+ }));
2961
+ rows.push(...looked.filter((row) => row !== undefined));
2962
+ }
2963
+ return rows.sort((a, b) => a.name.localeCompare(b.name));
2964
+ }
2965
+ async function outdatedCommand(args) {
2966
+ const { values, positionals: [dir = "."] } = parse(args, usage4, options3, { positionals: ["[dir]"], exits: EXITS3 });
2967
+ const folder = resolve3(dir);
2968
+ const manifest = Bun.file(join7(folder, "package.json"));
2969
+ if (!await manifest.exists())
2970
+ fail(`no package.json in ${folder}`);
2971
+ const lockPath = join7(folder, "bun.lock");
2972
+ const locked = existsSync3(lockPath) ? parseBunLock(await Bun.file(lockPath).text()) : [];
2973
+ const rows = await outdated(await manifest.text(), { minAgeDays: wholeNumber(values["min-age"], "min-age", DEFAULT_MIN_AGE_DAYS, 0), locked });
2974
+ const { shown, footer } = capped(rows, limitOf(values, OUTDATED_LIMIT));
2975
+ if (values.json === true)
2976
+ printJson(shown);
2977
+ else if (shown.length === 0)
2978
+ console.log("nothing outdated: every dependency is on its safe version");
2979
+ else
2980
+ console.log([table([["PACKAGE", "KIND", "CURRENT", "SAFE", "CHANGE", "PUBLISHED", "LATEST"], ...shown.map((each) => [each.name, each.kind, each.current, each.safe, each.change, each.published === undefined ? "-" : formatAge(each.published), each.latest ?? ""])], { header: true }), ...footer === undefined ? [] : [footer]].join(`
2981
+ `));
2982
+ return 0;
2458
2983
  }
2459
- var LOCKFILES, ACTIONS, OPTION_FLAGS, DIR_FLAGS, VALUE_FLAGS;
2460
- var init_pin = __esm(() => {
2461
- init_shell();
2462
- LOCKFILES = [
2463
- ["bun.lock", "bun"],
2464
- ["bun.lockb", "bun"],
2465
- ["pnpm-lock.yaml", "pnpm"],
2466
- ["yarn.lock", "yarn"],
2467
- ["package-lock.json", "npm"]
2468
- ];
2469
- ACTIONS = {
2470
- bun: actions({ install: ["install", "i"], add: ["add", "a"], remove: ["remove", "rm"], update: ["update"] }),
2471
- pnpm: actions({ install: ["install", "i"], add: ["add"], remove: ["remove", "rm"], update: ["update", "up"] }),
2472
- yarn: new Map([...actions({ install: ["", "install"], add: ["add"], remove: ["remove"], update: ["upgrade"] }), ["upgrade-interactive", null], ["import", null]]),
2473
- npm: actions({
2474
- install: ["install", "i", "in", "ins", "inst", "insta", "instal", "isnt", "isnta", "isntal", "isntall"],
2475
- add: ["add"],
2476
- frozen: ["ci", "clean-install", "ic", "install-clean", "isntall-clean"],
2477
- remove: ["uninstall", "unlink", "remove", "rm", "r", "un"],
2478
- update: ["update", "u", "up", "upgrade", "udpate"]
2479
- })
2480
- };
2481
- OPTION_FLAGS = new Map([
2482
- ["-D", "dev"],
2483
- ["--save-dev", "dev"],
2484
- ["--dev", "dev"],
2485
- ["-E", "exact"],
2486
- ["--save-exact", "exact"],
2487
- ["--exact", "exact"]
2488
- ]);
2489
- DIR_FLAGS = new Set(["--cwd", "-C", "--dir", "--prefix"]);
2490
- VALUE_FLAGS = new Set([...DIR_FLAGS, "--filter", "-F"]);
2491
- });
2984
+ var usage4 = `pkg outdated [dir]
2492
2985
 
2493
- // src/consumers.ts
2494
- import { existsSync, readdirSync, readFileSync } from "fs";
2495
- import { dirname as dirname2, join as join3, relative } from "path";
2496
- function packageFiles(dir, depth, found) {
2497
- let entries;
2498
- try {
2499
- entries = readdirSync(dir, { withFileTypes: true });
2500
- } catch {
2501
- return found;
2502
- }
2503
- for (const entry of entries) {
2504
- if (entry.isFile() && entry.name === "package.json")
2505
- found.push(join3(dir, entry.name));
2506
- else if (entry.isDirectory() && depth < MAX_DEPTH && !entry.name.startsWith(".") && !SKIPPED.has(entry.name))
2507
- packageFiles(join3(dir, entry.name), depth + 1, found);
2508
- }
2509
- return found;
2510
- }
2511
- function managerFor(dir, root) {
2512
- for (let at = dir;; at = dirname2(at)) {
2513
- const lockfile = LOCKFILES.find(([file]) => existsSync(join3(at, file)));
2514
- if (lockfile !== undefined)
2515
- return lockfile[1];
2516
- if (at === root || at === dirname2(at))
2517
- return "bun";
2518
- }
2519
- }
2520
- function findConsumers(name, root) {
2521
- const consumers = [];
2522
- for (const path of packageFiles(root, 0, []).sort()) {
2523
- let manifest;
2524
- try {
2525
- manifest = JSON.parse(readFileSync(path, "utf8"));
2526
- } catch {
2527
- continue;
2528
- }
2529
- const kind = KINDS.find((each) => typeof manifest[each]?.[name] === "string");
2530
- const spec = kind === undefined ? undefined : manifest[kind]?.[name];
2531
- if (kind !== undefined && spec !== undefined)
2532
- consumers.push({ dir: relative(root, dirname2(path)) || ".", path, kind, spec, manager: managerFor(dirname2(path), root) });
2533
- }
2534
- return consumers;
2535
- }
2536
- function isLocal(consumer) {
2537
- return LOCAL_SPEC.test(consumer.spec);
2538
- }
2539
- function addCommand(consumer, name, version) {
2540
- return ["bun", "add", ...KIND_FLAGS[consumer.kind], "--exact", "--no-cache", `${name}@${version}`];
2541
- }
2542
- var KINDS, SKIPPED, MAX_DEPTH = 5, LOCAL_SPEC, KIND_FLAGS;
2543
- var init_consumers = __esm(() => {
2544
- init_pin();
2545
- KINDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
2546
- SKIPPED = new Set(["node_modules", "dist", "build", "vendor", "target", "coverage"]);
2547
- LOCAL_SPEC = /[:/]/;
2548
- KIND_FLAGS = { dependencies: [], devDependencies: ["--dev"], optionalDependencies: ["--optional"], peerDependencies: ["--peer"] };
2986
+ Lists the dependencies of the package.json in the folder (the current one by default, registry versions only) that have a newer version safe to take: the highest stable one at least --min-age days old, the \`safe\` of \`pkg check\`. The version in use is the highest one the folder's bun.lock pins that the range allows, else the exact version written, else the first version in the range. A dependency whose newest version is still too fresh shows it under \`latest\`.
2987
+ It reads only the registry's metadata, no advisories, and changes nothing: \`bun add --exact name@version\` adopts a row.`, EXITS3, OUTDATED_LIMIT = 50, BATCH2 = 8, FIRST_VERSION, DOT, options3;
2988
+ var init_outdated = __esm(() => {
2989
+ init_src();
2990
+ init_analyze();
2991
+ init_config2();
2992
+ init_consumers();
2993
+ init_lockfile();
2994
+ init_registry();
2995
+ EXITS3 = [[0, "it listed what is outdated, nothing included"], [2, "the registry could not be reached or answered badly, no package.json, or a usage error"]];
2996
+ FIRST_VERSION = /\d+(?:\.\d+){0,2}(?:-[\w.-]+)?/;
2997
+ DOT = /[.-]/;
2998
+ options3 = {
2999
+ "min-age": { type: "string", description: `days a version must be old (${DEFAULT_MIN_AGE_DAYS})` },
3000
+ ...limitOptions(OUTDATED_LIMIT),
3001
+ json: jsonOption("[{name, kind, spec, current, safe, change, published, latest?}]")
3002
+ };
2549
3003
  });
2550
3004
 
2551
3005
  // src/commands/bump.ts
@@ -2554,9 +3008,9 @@ __export(exports_bump, {
2554
3008
  ROOT_OPTION: () => ROOT_OPTION,
2555
3009
  bump: () => bump
2556
3010
  });
2557
- import { existsSync as existsSync2 } from "fs";
2558
- import { homedir as homedir2 } from "os";
2559
- import { dirname as dirname3, join as join4, relative as relative2 } from "path";
3011
+ import { existsSync as existsSync4 } from "fs";
3012
+ import { homedir as homedir4 } from "os";
3013
+ import { dirname as dirname5, join as join8, relative as relative2 } from "path";
2560
3014
  async function lookup(name, registry, fetcher) {
2561
3015
  try {
2562
3016
  return { packument: await fetchPackument(name, registry, fetcher) };
@@ -2574,7 +3028,7 @@ async function servedVersion(name, version, settings) {
2574
3028
  const registry = registryFor(name, settings.configs);
2575
3029
  const host = new URL(registry.url).host;
2576
3030
  const started = Date.now();
2577
- const region = liveRegion((spinner) => [`${spinner} waiting for ${host} to serve ${name}@${version} ${formatElapsed(Date.now() - started)}`]);
3031
+ const region = liveRegion((spinner) => [`${spinner} waiting for ${host} to serve ${name}@${version ?? "latest"} ${formatElapsed(Date.now() - started)}`]);
2578
3032
  let lastError = "";
2579
3033
  try {
2580
3034
  for (;; ) {
@@ -2593,7 +3047,7 @@ async function servedVersion(name, version, settings) {
2593
3047
  return version;
2594
3048
  }
2595
3049
  if (Date.now() - started >= settings.timeout)
2596
- fail(`${host} does not serve ${name}@${version} after ${formatDuration(settings.timeout)}${lastError === "" ? "" : `, last error: ${lastError}`}`);
3050
+ fail(`${host} does not serve ${name}@${version ?? "latest"} after ${formatDuration(settings.timeout)}${lastError === "" ? "" : `, last error: ${lastError}`}`);
2597
3051
  region.redraw();
2598
3052
  await Bun.sleep(settings.poll ?? POLL);
2599
3053
  }
@@ -2616,11 +3070,11 @@ async function vet(name, version, settings) {
2616
3070
  return false;
2617
3071
  }
2618
3072
  function lockfileOf(dir, root) {
2619
- for (let at = dir;; at = dirname3(at)) {
2620
- const found = ["bun.lock", "bun.lockb"].find((file) => existsSync2(join4(at, file)));
3073
+ for (let at = dir;; at = dirname5(at)) {
3074
+ const found = ["bun.lock", "bun.lockb"].find((file) => existsSync4(join8(at, file)));
2621
3075
  if (found !== undefined)
2622
- return relative2(dir, join4(at, found));
2623
- if (at === root || at === dirname3(at))
3076
+ return relative2(dir, join8(at, found));
3077
+ if (at === root || at === dirname5(at))
2624
3078
  return;
2625
3079
  }
2626
3080
  }
@@ -2654,7 +3108,7 @@ async function bumpConsumers(name, version, settings) {
2654
3108
  note(`${local} non-registry dependenc${local === 1 ? "y" : "ies"} (workspace:, file:, git, URL, alias, catalog:)`, "left as they are");
2655
3109
  for (const consumer of consumers) {
2656
3110
  const argv = addCommand(consumer, name, version);
2657
- const where = join4(settings.root, consumer.dir);
3111
+ const where = join8(settings.root, consumer.dir);
2658
3112
  if (consumer.kind === "peerDependencies") {
2659
3113
  skipped.push(consumer.dir);
2660
3114
  note(consumer.dir, `peer range ${consumer.spec}, left as it is`);
@@ -2697,9 +3151,9 @@ async function bumpConsumers(name, version, settings) {
2697
3151
  return isClean;
2698
3152
  }
2699
3153
  async function bump(args) {
2700
- const { values, positionals: [name, asked] } = parse(args, usage3, options3, { positionals: ["<name>", "[version]"] });
3154
+ const { values, positionals: [name, asked] } = parse(args, usage5, options4, { positionals: ["<name>", "[version]"] });
2701
3155
  const settings = {
2702
- root: values.root ?? join4(homedir2(), "Workspace"),
3156
+ root: values.root ?? join8(homedir4(), "Workspace"),
2703
3157
  configs: await loadConfigs(),
2704
3158
  timeout: duration(values.timeout, "timeout", 10 * 60000),
2705
3159
  isDryRun: values["dry-run"] === true,
@@ -2713,14 +3167,14 @@ async function bump(args) {
2713
3167
  return 1;
2714
3168
  return await bumpConsumers(name, version, settings) ? 0 : 1;
2715
3169
  }
2716
- var usage3 = `pkg bump <name> [version]
3170
+ var usage5 = `pkg bump <name> [version]
2717
3171
 
2718
3172
  Waits until the registry serves the version (the latest when none is given, an exact version otherwise), vets it as \`pkg check\` does (advisories, licence allowlist, and a minimum age only when --min-age asks for one), then runs \`bun add --exact --no-cache\` in each folder under --root whose package.json depends on the package, the kind of dependency kept, a line per folder.
2719
3173
  A version the vetting blocks is refused before any folder is touched, unless --force is given.
2720
3174
  A dependency that is not a registry version (workspace:, link:, file:, git, a URL, an npm: alias, catalog:), a peer range, a folder already on the version and one whose lockfile pins another manager are left as they are, each said so.
2721
3175
  With --all-consumers a folder whose package.json or lockfile already has uncommitted changes is left alone, and each folder whose install went through then runs its own \`check\` (the check bin) and is committed with \`gc 'chore(deps): bump <name> to <version>' -- package.json <lockfile>\` only when that passes; a failing one is left uncommitted and listed in the summary with the skipped ones, and nothing is ever pushed.
2722
3176
 
2723
- Exits 0 when every install ran or was left as it is (with --all-consumers, and every check passed and every commit landed), 1 when the vetting refuses the version, an install, a check or a commit fails or the registry has no such package, 2 when the version never shows within the timeout (a registry that cannot be reached is retried until then) or on a usage error.`, ROOT_OPTION, options3, POLL = 5000;
3177
+ Exits 0 when every install ran or was left as it is (with --all-consumers, and every check passed and every commit landed), 1 when the vetting refuses the version, an install, a check or a commit fails or the registry has no such package, 2 when the version never shows within the timeout (a registry that cannot be reached is retried until then) or on a usage error.`, ROOT_OPTION, options4, POLL = 5000;
2724
3178
  var init_bump = __esm(() => {
2725
3179
  init_src();
2726
3180
  init_progress();
@@ -2730,7 +3184,7 @@ var init_bump = __esm(() => {
2730
3184
  init_semver();
2731
3185
  init_check();
2732
3186
  ROOT_OPTION = { type: "string", description: "the folder holding the repositories (~/Workspace)" };
2733
- options3 = {
3187
+ options4 = {
2734
3188
  root: ROOT_OPTION,
2735
3189
  timeout: { type: "string", short: "t", description: "how long to wait for the registry (10m): 30s, 5m, 1h" },
2736
3190
  "all-consumers": { type: "boolean", description: "after each install run the folder's check and commit it through gc when it passes" },
@@ -2745,11 +3199,11 @@ var exports_consumers = {};
2745
3199
  __export(exports_consumers, {
2746
3200
  consumers: () => consumers
2747
3201
  });
2748
- import { homedir as homedir3 } from "os";
2749
- import { join as join5 } from "path";
3202
+ import { homedir as homedir5 } from "os";
3203
+ import { join as join9 } from "path";
2750
3204
  function consumers(args) {
2751
- const { values, positionals: [name] } = parse(args, usage4, options4, { positionals: ["<name>"] });
2752
- const root = values.root ?? join5(homedir3(), "Workspace");
3205
+ const { values, positionals: [name] } = parse(args, usage6, options5, { positionals: ["<name>"] });
3206
+ const root = values.root ?? join9(homedir5(), "Workspace");
2753
3207
  const { shown, footer } = capped(findConsumers(name, root), limitOf(values, CONSUMERS_LIMIT));
2754
3208
  if (values.json === true)
2755
3209
  printJson(shown);
@@ -2760,16 +3214,16 @@ function consumers(args) {
2760
3214
  `));
2761
3215
  return 0;
2762
3216
  }
2763
- var usage4 = `pkg consumers <name>
3217
+ var usage6 = `pkg consumers <name>
2764
3218
 
2765
3219
  Lists the folders under --root whose package.json depends on the package: the folder, the kind of dependency, the version it asks for and the manager its lockfile pins, 30 at most with the count of the rest after them (--limit <n>, --full for all; --json is never cut unless --limit is given).
2766
3220
 
2767
- Exits 0 when it listed them, 2 on a usage error.`, CONSUMERS_LIMIT = 30, options4;
3221
+ Exits 0 when it listed them, 2 on a usage error.`, CONSUMERS_LIMIT = 30, options5;
2768
3222
  var init_consumers2 = __esm(() => {
2769
3223
  init_src();
2770
3224
  init_consumers();
2771
3225
  init_bump();
2772
- options4 = {
3226
+ options5 = {
2773
3227
  root: ROOT_OPTION,
2774
3228
  ...limitOptions(CONSUMERS_LIMIT),
2775
3229
  json: jsonOption("[{dir, path, kind, spec, manager}]")
@@ -2778,14 +3232,9 @@ var init_consumers2 = __esm(() => {
2778
3232
 
2779
3233
  // src/cli.ts
2780
3234
  init_src();
2781
- import process10 from "process";
2782
- // package.json
2783
- var version = "0.7.0";
2784
-
2785
- // src/cli.ts
2786
- process10.exitCode = await runCli({
3235
+ import process15 from "process";
3236
+ process15.exitCode = await runCli({
2787
3237
  name: "pkg",
2788
- version,
2789
3238
  about: "checks npm packages before they are added, and moves a lib's consumers onto a new release",
2790
3239
  replaces: "bun add on trust, and a bun info and sleep loop then a bun add per consumer",
2791
3240
  examples: [["pkg zod", "the name, the newest version two weeks old, its licence and scripts"], ["pkg bump @kaynooo/cli-kit --dry-run", "the consumers a release would move"]],
@@ -2793,6 +3242,7 @@ process10.exitCode = await runCli({
2793
3242
  commands: {
2794
3243
  check: { summary: "check packages before they are added (`pkg <name>` alone runs it)", run: async (args) => (await Promise.resolve().then(() => (init_check(), exports_check))).check(args) },
2795
3244
  audit: { summary: "check every package a bun.lock pins, with its known advisories", run: async (args) => (await Promise.resolve().then(() => (init_audit(), exports_audit))).audit(args) },
3245
+ outdated: { summary: "list the dependencies of this folder that have a newer version safe to take", run: async (args) => (await Promise.resolve().then(() => (init_outdated(), exports_outdated))).outdatedCommand(args) },
2796
3246
  bump: { summary: "wait for a release on the registry, then bun add it in every consumer", run: async (args) => (await Promise.resolve().then(() => (init_bump(), exports_bump))).bump(args) },
2797
3247
  consumers: { summary: "list the folders depending on a package, with the version each asks for", run: async (args) => (await Promise.resolve().then(() => (init_consumers2(), exports_consumers))).consumers(args) }
2798
3248
  }