@kaynooo/cli-pkg 0.7.1 → 0.8.1

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 +683 -451
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env -S bun --no-env-file
2
2
  // @bun
3
3
  var __defProp = Object.defineProperty;
4
4
  var __returnValue = (v) => v;
@@ -26,6 +26,18 @@ var __esm = (fn, res, err) => () => {
26
26
  return res;
27
27
  };
28
28
 
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
+
29
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();
@@ -293,30 +305,30 @@ var init_colors = __esm(() => {
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)));
@@ -422,21 +434,27 @@ var init_format = __esm(() => {
422
434
  });
423
435
 
424
436
  // ../kit/src/help.ts
425
- import process3 from "process";
426
- function usageLine(usage, colors = paint(process3.stdout)) {
437
+ import process4 from "process";
438
+ function usageLine(usage, colors = paint(process4.stdout)) {
427
439
  return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
428
440
  }
429
- function helpText(usage, options, colors = paint(process3.stdout), { examples = [], replaces } = {}) {
430
- return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
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(`
431
443
  `);
432
444
  }
433
- function replacesRows(replaces, colors = paint(process3.stdout)) {
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)) {
434
452
  return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
435
453
  }
436
- function wantsHelpJson(env = process3.env) {
454
+ function wantsHelpJson(env = process4.env) {
437
455
  return env.KIT_HELP === "json";
438
456
  }
439
- function description(usage, { examples = [], replaces } = {}) {
457
+ function description(usage, { examples = [], replaces, exits } = {}, options) {
440
458
  const lines = usage.replace(USAGE_PREFIX, "").split(`
441
459
  `);
442
460
  const first = lines[0];
@@ -447,15 +465,17 @@ function description(usage, { examples = [], replaces } = {}) {
447
465
  synopsis: colon === -1 ? first : first.slice(0, colon),
448
466
  summary: colon === -1 ? fallback : first.slice(colon + 2),
449
467
  ...replaces === undefined ? {} : { replaces },
450
- examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
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 } })) }
451
471
  };
452
472
  }
453
- function exampleRows(examples, colors = paint(process3.stdout)) {
473
+ function exampleRows(examples, colors = paint(process4.stdout)) {
454
474
  if (examples.length === 0)
455
475
  return [];
456
476
  return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
457
477
  }
458
- function optionRows(options, colors = paint(process3.stdout)) {
478
+ function optionRows(options, colors = paint(process4.stdout)) {
459
479
  const rows = Object.entries(options).map(([name, option]) => {
460
480
  const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
461
481
  const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
@@ -498,6 +518,8 @@ var init_text = __esm(() => {
498
518
  });
499
519
 
500
520
  // ../kit/src/input.ts
521
+ import { fstatSync } from "fs";
522
+ import process5 from "process";
501
523
  function wholeNumber(value, name, fallback, min = 1) {
502
524
  if (value === undefined)
503
525
  return fallback;
@@ -515,6 +537,21 @@ function duration(value, name, fallback, unit = "s") {
515
537
  badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
516
538
  return ms;
517
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
+ }
518
555
  var WHOLE, DURATION, UNIT_MS;
519
556
  var init_input = __esm(() => {
520
557
  init_args();
@@ -524,7 +561,7 @@ var init_input = __esm(() => {
524
561
  });
525
562
 
526
563
  // ../kit/src/args.ts
527
- import process4 from "process";
564
+ import process6 from "process";
528
565
  import { parseArgs } from "util";
529
566
  function jsonOption(holds) {
530
567
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
@@ -546,7 +583,7 @@ class Completing {
546
583
  this.candidates = candidates;
547
584
  }
548
585
  }
549
- function applyOutput(color, plain, env = process4.env) {
586
+ function applyOutput(color, plain, env = process6.env) {
550
587
  const when = plain === true ? "never" : color ?? "auto";
551
588
  if (when === "auto")
552
589
  return;
@@ -569,7 +606,7 @@ function parse(args, usage, options, settings = {}) {
569
606
  const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
570
607
  let parsed;
571
608
  try {
572
- parsed = parseArgs({ args: joinDashValues(withBare(own, all), 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 });
573
610
  } catch (error) {
574
611
  const message = parseError(error);
575
612
  throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
@@ -577,7 +614,7 @@ function parse(args, usage, options, settings = {}) {
577
614
  const values = parsed.values;
578
615
  applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
579
616
  if (values.help === true) {
580
- console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
617
+ console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings, listing)) : helpText(usage, listing, undefined, settings));
581
618
  throw new HelpShown;
582
619
  }
583
620
  checkChoices(parsed.values, all);
@@ -619,6 +656,18 @@ function listed(options, allowNegative) {
619
656
  return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
620
657
  }));
621
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
+ }
622
671
  function withBare(args, options) {
623
672
  const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
624
673
  if (bare.size === 0)
@@ -745,7 +794,7 @@ var init_chart = __esm(() => {
745
794
 
746
795
  // ../kit/src/process.ts
747
796
  import { constants } from "os";
748
- import process5 from "process";
797
+ import process7 from "process";
749
798
  function exitCodeOf(code, signal) {
750
799
  if (code !== null)
751
800
  return code;
@@ -765,7 +814,7 @@ function holdSignals(cleanup) {
765
814
  },
766
815
  release: () => {
767
816
  for (const signal of SIGNALS)
768
- process5.off(signal, onSignal);
817
+ process7.off(signal, onSignal);
769
818
  }
770
819
  };
771
820
  function onSignal(signal) {
@@ -778,10 +827,10 @@ function holdSignals(cleanup) {
778
827
  return;
779
828
  held.release();
780
829
  cleanup(signal);
781
- process5.kill(process5.pid, signal);
830
+ process7.kill(process7.pid, signal);
782
831
  }
783
832
  for (const signal of SIGNALS)
784
- process5.on(signal, onSignal);
833
+ process7.on(signal, onSignal);
785
834
  return held;
786
835
  }
787
836
  async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal, maxBytes = Infinity, onChunk, onStart } = {}) {
@@ -795,7 +844,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
795
844
  return;
796
845
  try {
797
846
  if (grouped)
798
- process5.kill(-proc.pid, killing);
847
+ process7.kill(-proc.pid, killing);
799
848
  else
800
849
  proc.kill(killing);
801
850
  } catch {}
@@ -868,8 +917,8 @@ var init_process = __esm(() => {
868
917
  });
869
918
 
870
919
  // ../kit/src/progress.ts
871
- import process6 from "process";
872
- function liveRegion(render, stream = process6.stderr, env = process6.env) {
920
+ import process8 from "process";
921
+ function liveRegion(render, stream = process8.stderr, env = process8.env) {
873
922
  if (!takesControlCodes(stream, env))
874
923
  return { redraw: () => {
875
924
  return;
@@ -910,11 +959,11 @@ function liveRegion(render, stream = process6.stderr, env = process6.env) {
910
959
  clear();
911
960
  restore();
912
961
  release();
913
- process6.off("exit", onExit);
962
+ process8.off("exit", onExit);
914
963
  signals.release();
915
964
  }
916
965
  timer.unref();
917
- process6.on("exit", onExit);
966
+ process8.on("exit", onExit);
918
967
  return {
919
968
  redraw: draw,
920
969
  print: (text) => {
@@ -953,14 +1002,14 @@ function cutToWidth(line, width) {
953
1002
  return `${kept}${RESET_STYLE}${linked ? CLOSE_LINK : ""}`;
954
1003
  }
955
1004
  function swallowInput() {
956
- const input = process6.stdin;
1005
+ const input = process8.stdin;
957
1006
  if (!input.isTTY)
958
1007
  return () => {
959
1008
  return;
960
1009
  };
961
1010
  const onData = (data) => {
962
1011
  if (data.includes(CTRL_C))
963
- process6.kill(process6.pid, "SIGINT");
1012
+ process8.kill(process8.pid, "SIGINT");
964
1013
  };
965
1014
  input.setRawMode(true);
966
1015
  input.on("data", onData);
@@ -976,7 +1025,7 @@ function swallowInput() {
976
1025
  };
977
1026
  }
978
1027
  function interceptOutput(clear) {
979
- const targets = [...CONSOLE_METHODS.map((name) => [console, name]), [process6.stdout, "write"], [process6.stderr, "write"]];
1028
+ const targets = [...CONSOLE_METHODS.map((name) => [console, name]), [process8.stdout, "write"], [process8.stderr, "write"]];
980
1029
  const wrapped = targets.map(([target, name]) => {
981
1030
  const original = Reflect.get(target, name);
982
1031
  const wrapper = (...args) => {
@@ -1030,18 +1079,18 @@ var init_logs = __esm(() => {
1030
1079
  import { appendFileSync, mkdirSync } from "fs";
1031
1080
  import { homedir } from "os";
1032
1081
  import { dirname, isAbsolute, join } from "path";
1033
- import process7 from "process";
1082
+ import process9 from "process";
1034
1083
  function noteUsageError(message) {
1035
1084
  usageError = message;
1036
1085
  }
1037
- function stateHome(env = process7.env) {
1086
+ function stateHome(env = process9.env) {
1038
1087
  const dir = env.XDG_STATE_HOME;
1039
1088
  return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
1040
1089
  }
1041
- function usagePath(env = process7.env) {
1090
+ function usagePath(env = process9.env) {
1042
1091
  return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
1043
1092
  }
1044
- function usageRecorded(env = process7.env) {
1093
+ function usageRecorded(env = process9.env) {
1045
1094
  const off = env.KAYNOOO_CLI_NO_USAGE;
1046
1095
  return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
1047
1096
  }
@@ -1055,7 +1104,7 @@ function usageEntry(bin, command, argv, code, now = new Date) {
1055
1104
  usageError = undefined;
1056
1105
  return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
1057
1106
  }
1058
- function recordUsage(entry, env = process7.env) {
1107
+ function recordUsage(entry, env = process9.env) {
1059
1108
  if (!usageRecorded(env))
1060
1109
  return;
1061
1110
  try {
@@ -1073,7 +1122,9 @@ var init_usage = __esm(() => {
1073
1122
  });
1074
1123
 
1075
1124
  // ../kit/src/cli.ts
1076
- import process8 from "process";
1125
+ import { readFileSync, realpathSync } from "fs";
1126
+ import { dirname as dirname2, join as join2 } from "path";
1127
+ import process10 from "process";
1077
1128
  async function commandRows(all, colors) {
1078
1129
  const commands = shown(all);
1079
1130
  const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
@@ -1148,8 +1199,8 @@ async function help2({ path, header, summary: lazySummary, commands, defaultComm
1148
1199
  `);
1149
1200
  }
1150
1201
  function printError(prefix, message, hint, cause) {
1151
- const colors = paint(process8.stderr);
1152
- const styled = takesControlCodes(process8.stderr);
1202
+ const colors = paint(process10.stderr);
1203
+ const styled = takesControlCodes(process10.stderr);
1153
1204
  const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
1154
1205
  const under = (label, text, tint) => text === undefined ? "" : `
1155
1206
  ${styled ? tint(label) : `${label}:`} ${text}`;
@@ -1192,11 +1243,11 @@ async function dispatch(level, typed, { version, redact }) {
1192
1243
  }
1193
1244
  }
1194
1245
  if (name === undefined && level.path.length > 1) {
1195
- console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process8.stderr));
1246
+ console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process10.stderr));
1196
1247
  return 2;
1197
1248
  }
1198
1249
  if (name === undefined || name === "help" || name === "--help" || name === "-h") {
1199
- console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process8.stdout));
1250
+ console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process10.stdout));
1200
1251
  return 0;
1201
1252
  }
1202
1253
  if (level.path.length === 1 && (name === "--version" || name === "-v")) {
@@ -1310,10 +1361,22 @@ async function printCandidates(found) {
1310
1361
  console.log(line);
1311
1362
  return 0;
1312
1363
  }
1313
- async function runCli(cli, argv = process8.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();
1314
1377
  if (argv[0] === COMPLETE)
1315
1378
  return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
1316
- const code = await 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, runBare: cli.runBare === true, 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 });
1317
1380
  if (cli.usage !== false) {
1318
1381
  try {
1319
1382
  recordUsage(usageEntry(cli.name, commandPath(scopeOf(cli.commands, cli.extraCommands, cli.aliases), argv), argv, code));
@@ -1348,7 +1411,6 @@ var init_cli = __esm(() => {
1348
1411
  };
1349
1412
  TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
1350
1413
  });
1351
-
1352
1414
  // ../kit/src/config.ts
1353
1415
  var init_config = __esm(() => {
1354
1416
  init_args();
@@ -1399,8 +1461,33 @@ var init_sessions = __esm(() => {
1399
1461
  ACCOUNT_DIRS = new Glob("*/sessions");
1400
1462
  });
1401
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
+
1402
1488
  // ../kit/src/index.ts
1403
1489
  var init_src = __esm(() => {
1490
+ init_advisory();
1404
1491
  init_args();
1405
1492
  init_chart();
1406
1493
  init_ci();
@@ -1413,216 +1500,12 @@ var init_src = __esm(() => {
1413
1500
  init_process();
1414
1501
  init_secrets();
1415
1502
  init_sessions();
1503
+ init_size();
1416
1504
  init_style();
1417
1505
  init_text();
1418
1506
  init_tool();
1419
1507
  init_usage();
1420
- });
1421
-
1422
- // src/registry.ts
1423
- function isPublic(registry) {
1424
- return registry.url.replace(TRAILING_SLASHES, "") === PUBLIC_REGISTRY;
1425
- }
1426
- function unreachable(message) {
1427
- return fail(`could not check: ${message}`, 2);
1428
- }
1429
- function requestInit(token) {
1430
- return { headers: { accept: "application/json", ...token === undefined ? {} : { authorization: `Bearer ${token}` } }, signal: AbortSignal.timeout(REQUEST_TIMEOUT) };
1431
- }
1432
- async function get(fetcher, url, token) {
1433
- try {
1434
- return await fetcher(url, requestInit(token));
1435
- } catch (error) {
1436
- return unreachable(`cannot reach ${new URL(url).host}: ${error instanceof Error ? error.message : String(error)}`);
1437
- }
1438
- }
1439
- function isValidName(name) {
1440
- return PACKAGE_NAME.test(name) && name.length <= 214;
1441
- }
1442
- async function fetchPackument(name, registry, fetcher = fetch) {
1443
- if (!isValidName(name))
1444
- fail(`${name} is not a valid npm package name`);
1445
- const response = await get(fetcher, `${registry.url.replace(TRAILING_SLASHES, "")}/${encodeURIComponent(name).replace("%40", "@").replace("%2F", "%2f")}`, registry.token);
1446
- if (response.status === 404)
1447
- return;
1448
- if (!response.ok)
1449
- unreachable(`${new URL(registry.url).host} answered ${response.status} for ${name}`);
1450
- let packument;
1451
- try {
1452
- packument = await response.json();
1453
- } catch {
1454
- return unreachable(`${new URL(registry.url).host} answered ${name} with something that is not JSON`);
1455
- }
1456
- if (packument?.name !== name)
1457
- unreachable(`${new URL(registry.url).host} answered ${name} with the metadata of ${String(packument?.name)}`);
1458
- return packument;
1459
- }
1460
- async function fetchDownloads(name, fetcher = fetch) {
1461
- const response = await get(fetcher, `${DOWNLOADS_API}/${encodeURIComponent(name)}`);
1462
- if (!response.ok)
1463
- return;
1464
- try {
1465
- return (await response.json()).downloads;
1466
- } catch {
1467
- return;
1468
- }
1469
- }
1470
- var PUBLIC_REGISTRY = "https://registry.npmjs.org", DOWNLOADS_API = "https://api.npmjs.org/downloads/point/last-week", TRAILING_SLASHES, REQUEST_TIMEOUT = 1e4, PACKAGE_NAME;
1471
- var init_registry = __esm(() => {
1472
- init_src();
1473
- TRAILING_SLASHES = /\/+$/;
1474
- PACKAGE_NAME = /^(?:@[\w~-][\w.~-]*\/)?[\w~-][\w.~-]*$/;
1475
- });
1476
-
1477
- // src/config.ts
1478
- import { homedir as homedir2 } from "os";
1479
- import { dirname as dirname2, join as join2 } from "path";
1480
- import process10 from "process";
1481
- function expand2(value, env) {
1482
- let missing = false;
1483
- const expanded = value.replace(ENV_REFERENCE, (_, braced, bare) => {
1484
- const found = env[braced ?? bare ?? ""];
1485
- missing ||= found === undefined;
1486
- return found ?? "";
1487
- });
1488
- return missing ? undefined : expanded;
1489
- }
1490
- function literal(value) {
1491
- return expand2(value, {}) === value ? value : undefined;
1492
- }
1493
- function parseNpmrc(text, env) {
1494
- const config = { scopes: {}, tokens: {}, scopeTokens: {} };
1495
- for (const line of text.split(`
1496
- `)) {
1497
- const at = line.indexOf("=");
1498
- if (at === -1 || COMMENT.test(line))
1499
- continue;
1500
- const key = line.slice(0, at).trim();
1501
- const scope = SCOPE_REGISTRY.exec(key)?.[1];
1502
- const tokenFor = AUTH_TOKEN.exec(key)?.[1];
1503
- const raw = line.slice(at + 1).trim().replace(QUOTES, "$2");
1504
- const value = tokenFor === undefined ? literal(raw) : expand2(raw, env);
1505
- if (value === undefined || value === "")
1506
- continue;
1507
- if (scope !== undefined)
1508
- config.scopes[scope] = value;
1509
- else if (tokenFor !== undefined)
1510
- config.tokens[tokenFor] = value;
1511
- else if (key === "registry")
1512
- config.registry = value;
1513
- }
1514
- return config;
1515
- }
1516
- function bunEntry(entry, env) {
1517
- const url = typeof entry === "string" ? entry : entry.url;
1518
- const token = typeof entry === "string" || entry.token === undefined ? undefined : expand2(entry.token, env);
1519
- return { ...url === undefined || literal(url) === undefined ? {} : { url }, ...token === undefined ? {} : { token } };
1520
- }
1521
- function parseBunfig(text, env) {
1522
- const config = { scopes: {}, tokens: {}, scopeTokens: {} };
1523
- const install = Bun.TOML.parse(text).install;
1524
- if (install?.registry !== undefined) {
1525
- const { url, token } = bunEntry(install.registry, env);
1526
- if (url !== undefined)
1527
- config.registry = url;
1528
- if (url !== undefined && token !== undefined)
1529
- config.tokens[url.replace(PROTOCOL, "")] = token;
1530
- }
1531
- for (const [scope, entry] of Object.entries(install?.scopes ?? {})) {
1532
- const { url, token } = bunEntry(entry, env);
1533
- if (url !== undefined)
1534
- config.scopes[scope.startsWith("@") ? scope : `@${scope}`] = url;
1535
- if (token !== undefined)
1536
- config.scopeTokens[scope.startsWith("@") ? scope : `@${scope}`] = token;
1537
- }
1538
- return config;
1539
- }
1540
- function tokenFor(config, url) {
1541
- const address = `${url.replace(PROTOCOL, "").replace(TRAILING_SLASHES2, "")}/`;
1542
- const best = Object.keys(config.tokens).filter((key) => address.startsWith(`${key.replace(TRAILING_SLASHES2, "")}/`)).sort((a, b) => b.length - a.length)[0];
1543
- return best === undefined ? undefined : config.tokens[best];
1544
- }
1545
- function mayCarryToken(url) {
1546
- const { protocol, hostname } = new URL(url);
1547
- return protocol === "https:" || LOCAL_HOSTS.has(hostname);
1548
- }
1549
- function registryFor(name, configs) {
1550
- const scope = name.startsWith("@") ? name.slice(0, name.indexOf("/")) : undefined;
1551
- const scoped = scope === undefined ? undefined : configs.find((config) => config.scopes[scope] !== undefined);
1552
- const url = (scope === undefined ? undefined : scoped?.scopes[scope]) ?? configs.find((config) => config.registry !== undefined)?.registry;
1553
- if (url === undefined)
1554
- return { url: PUBLIC_REGISTRY };
1555
- const token = (scope === undefined ? undefined : scoped?.scopeTokens[scope]) ?? configs.map((other) => tokenFor(other, url)).find((found) => found !== undefined);
1556
- return { url, token: mayCarryToken(url) ? token : undefined };
1557
- }
1558
- function readable(path, parse) {
1559
- try {
1560
- return parse();
1561
- } catch (error) {
1562
- return unreachable(`${path} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
1563
- }
1564
- }
1565
- async function readIfThere(path) {
1566
- const file = Bun.file(path);
1567
- return await file.exists() ? file.text() : undefined;
1568
- }
1569
- async function loadConfigs(cwd = process10.cwd(), env = process10.env, home = homedir2()) {
1570
- const directories = [];
1571
- for (let dir = cwd;!directories.includes(dir); dir = dirname2(dir))
1572
- directories.push(dir);
1573
- if (!directories.includes(home))
1574
- directories.push(home);
1575
- const configs = [];
1576
- for (const dir of directories) {
1577
- const bunfig = await readIfThere(join2(dir, "bunfig.toml"));
1578
- const npmrc = await readIfThere(join2(dir, ".npmrc"));
1579
- if (bunfig !== undefined)
1580
- configs.push(readable(join2(dir, "bunfig.toml"), () => parseBunfig(bunfig, env)));
1581
- if (npmrc !== undefined)
1582
- configs.push(parseNpmrc(npmrc, env));
1583
- }
1584
- return configs;
1585
- }
1586
- function isSeverity(value) {
1587
- return SEVERITIES.includes(value);
1588
- }
1589
- function parseSettings(text) {
1590
- const raw = Bun.JSONC.parse(text);
1591
- const settings = {};
1592
- if (Array.isArray(raw?.licenses) && raw.licenses.every((each) => typeof each === "string"))
1593
- settings.licenses = raw.licenses;
1594
- if (isSeverity(raw?.blockSeverity))
1595
- settings.blockSeverity = raw.blockSeverity;
1596
- return settings;
1597
- }
1598
- async function loadSettings(cwd = process10.cwd(), home = homedir2()) {
1599
- const files = [];
1600
- for (let dir = cwd;!files.includes(join2(dir, SETTINGS_FILE)); dir = dirname2(dir))
1601
- files.push(join2(dir, SETTINGS_FILE));
1602
- files.push(join2(home, ".config", "pkg", "config.json"));
1603
- const settings = {};
1604
- for (const file of files) {
1605
- const text = await readIfThere(file);
1606
- if (text === undefined)
1607
- continue;
1608
- const found = readable(file, () => parseSettings(text));
1609
- settings.licenses ??= found.licenses;
1610
- settings.blockSeverity ??= found.blockSeverity;
1611
- }
1612
- return settings;
1613
- }
1614
- var ENV_REFERENCE, COMMENT, SCOPE_REGISTRY, AUTH_TOKEN, PROTOCOL, TRAILING_SLASHES2, LOCAL_HOSTS, QUOTES, SEVERITIES, DEFAULT_BLOCK_SEVERITY = "high", SETTINGS_FILE = ".pkgrc.json";
1615
- var init_config2 = __esm(() => {
1616
- init_registry();
1617
- ENV_REFERENCE = /\$\{(\w+)\}|\$(\w+)/g;
1618
- COMMENT = /^\s*[#;]/;
1619
- SCOPE_REGISTRY = /^(@[^:]+):registry$/;
1620
- AUTH_TOKEN = /^(\/\/.+?):_authToken$/;
1621
- PROTOCOL = /^\w+:/;
1622
- TRAILING_SLASHES2 = /\/+$/;
1623
- LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
1624
- QUOTES = /^(["'])(.*)\1$/;
1625
- SEVERITIES = ["low", "moderate", "high", "critical"];
1508
+ init_user_config();
1626
1509
  });
1627
1510
 
1628
1511
  // src/semver.ts
@@ -1667,15 +1550,12 @@ var init_semver = __esm(() => {
1667
1550
  });
1668
1551
 
1669
1552
  // src/advisories.ts
1670
- function severityRank(severity) {
1671
- return SEVERITIES.indexOf(severity);
1672
- }
1673
1553
  function fixedVersion(vulnerable, version) {
1674
1554
  const range = vulnerable.split("||").map((each) => each.trim()).find((each) => inRange(version, each));
1675
1555
  return range === undefined ? undefined : UPPER_BOUND.exec(range)?.[1];
1676
1556
  }
1677
1557
  async function fetchAdvisories(versions, registry, fetcher = fetch) {
1678
- const url = `${registry.url.replace(TRAILING_SLASHES3, "")}${BULK_PATH}`;
1558
+ const url = `${registry.url.replace(TRAILING_SLASHES, "")}${BULK_PATH}`;
1679
1559
  const host = new URL(registry.url).host;
1680
1560
  let response;
1681
1561
  try {
@@ -1683,7 +1563,7 @@ async function fetchAdvisories(versions, registry, fetcher = fetch) {
1683
1563
  method: "POST",
1684
1564
  headers: { accept: "application/json", "content-type": "application/json", ...registry.token === undefined ? {} : { authorization: `Bearer ${registry.token}` } },
1685
1565
  body: JSON.stringify(versions),
1686
- signal: AbortSignal.timeout(REQUEST_TIMEOUT2)
1566
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT)
1687
1567
  });
1688
1568
  } catch (error) {
1689
1569
  return { found: {}, unavailable: `cannot reach ${host} for advisories: ${error instanceof Error ? error.message : String(error)}` };
@@ -1717,13 +1597,13 @@ function advisoriesFor(advisories, version) {
1717
1597
  return (advisories ?? []).filter((each) => inRange(version, each.vulnerable)).map((each) => ({ ...each, fixed: fixedVersion(each.vulnerable, version) }));
1718
1598
  }
1719
1599
  function filterAdvisories(advisories, level, ignore = []) {
1720
- 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));
1721
1601
  }
1722
- 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;
1723
1603
  var init_advisories = __esm(() => {
1724
- init_config2();
1604
+ init_src();
1725
1605
  init_semver();
1726
- TRAILING_SLASHES3 = /\/+$/;
1606
+ TRAILING_SLASHES = /\/+$/;
1727
1607
  GHSA = /GHSA-[\w-]+$/;
1728
1608
  UPPER_BOUND = /(?:^|\s)<([\w.+-]+)/;
1729
1609
  NO_ENDPOINT = new Set([404, 405, 501]);
@@ -2289,17 +2169,72 @@ var init_nearmiss = __esm(() => {
2289
2169
  SEPARATORS = /[-_.]/g;
2290
2170
  });
2291
2171
 
2292
- // src/analyze.ts
2293
- function parseSpec(text) {
2294
- const at = text.search(VERSION_SEPARATOR);
2295
- 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;
2296
2175
  }
2297
- function isStable(version) {
2298
- return !version.includes("-");
2176
+ function unreachable(message) {
2177
+ return fail(`could not check: ${message}`, 2);
2299
2178
  }
2300
- function resolve(packument, request) {
2301
- const tags = packument["dist-tags"] ?? {};
2302
- const versions = Object.keys(packument.versions ?? {});
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 ?? {});
2303
2238
  const wanted = request === undefined || request === "" ? "latest" : request;
2304
2239
  if (tags[wanted] !== undefined)
2305
2240
  return tags[wanted];
@@ -2408,6 +2343,310 @@ var init_analyze = __esm(() => {
2408
2343
  VERSION_SEPARATOR = /(?<=.)@/;
2409
2344
  });
2410
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
+
2411
2650
  // src/commands/check.ts
2412
2651
  var exports_check = {};
2413
2652
  __export(exports_check, {
@@ -2457,13 +2696,20 @@ function render(report, now = Date.now()) {
2457
2696
  ].join(`
2458
2697
  `);
2459
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
+ }
2460
2705
  async function check(args) {
2461
- const parsed = parse(args, usage2, options);
2462
- 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);
2463
2709
  if (positionals.length === 0)
2464
- fail(`a package name is needed
2710
+ fail(values.from === undefined ? `a package name is needed
2465
2711
 
2466
- ${usage2}`);
2712
+ ${usage2}` : `${values.from} has no registry dependency to check`);
2467
2713
  const context = { configs: await loadConfigs(), settings: await loadSettings(), minAgeDays: wholeNumber(values["min-age"], "min-age", DEFAULT_MIN_AGE_DAYS, 0) };
2468
2714
  const reports = await Promise.all(positionals.map(async (text) => checkPackage(text, context)));
2469
2715
  if (values.json === true)
@@ -2474,23 +2720,27 @@ ${usage2}`);
2474
2720
  `));
2475
2721
  return reports.some((report) => report.blocked) ? 1 : 0;
2476
2722
  }
2477
- var usage2 = `pkg [check] <name>[@version]...
2723
+ var usage2 = `pkg [check] <name>[@version]... | --from <package.json> | -
2478
2724
 
2479
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.
2480
2726
 
2481
- 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.
2482
2728
 
2483
- 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;
2484
2730
  var init_check = __esm(() => {
2485
2731
  init_src();
2486
2732
  init_advisories();
2487
2733
  init_analyze();
2488
2734
  init_config2();
2735
+ init_consumers();
2489
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"]];
2490
2738
  options = {
2491
2739
  json: jsonOption(),
2492
- "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" }
2493
2742
  };
2743
+ WORDS = /\s+/;
2494
2744
  });
2495
2745
 
2496
2746
  // src/lockfile.ts
@@ -2516,6 +2766,42 @@ function parseBunLock(text) {
2516
2766
  }
2517
2767
  return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name) || Bun.semver.order(a.version, b.version));
2518
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
+ }
2519
2805
  var NOT_REGISTRY, VERSION_SEPARATOR2, NPM_ALIAS = "npm:";
2520
2806
  var init_lockfile = __esm(() => {
2521
2807
  NOT_REGISTRY = /^(?:workspace|link|file|github|git|http|https):|^[\w.-]+\/[\w.-]+$/;
@@ -2527,8 +2813,8 @@ var exports_audit = {};
2527
2813
  __export(exports_audit, {
2528
2814
  audit: () => audit
2529
2815
  });
2530
- import { join as join3, resolve as resolve2 } from "path";
2531
- import process11 from "process";
2816
+ import { join as join6, resolve as resolve2 } from "path";
2817
+ import process14 from "process";
2532
2818
  function hasFinding(audited) {
2533
2819
  return audited.blocked || audited.warnings.length > 0 || audited.advisories.length > 0;
2534
2820
  }
@@ -2583,15 +2869,17 @@ function renderAudit(findings, total) {
2583
2869
  `);
2584
2870
  }
2585
2871
  async function audit(args) {
2586
- const { values, positionals: [asked] } = parse(args, usage3, options2, { positionals: ["[dir]"] });
2587
- 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";
2588
2874
  if (!isSeverity(level))
2589
- fail(`--audit-level takes ${SEVERITIES.join(", ")}, not ${level}`);
2590
- const dir = resolve2(asked ?? process11.cwd());
2591
- const lock = Bun.file(join3(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"));
2592
2878
  if (!await lock.exists())
2593
2879
  fail(`no bun.lock in ${dir}`);
2594
- 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);
2595
2883
  const context = {
2596
2884
  configs: await loadConfigs(dir),
2597
2885
  settings: await loadSettings(dir),
@@ -2615,8 +2903,7 @@ var usage3 = `pkg audit [dir]
2615
2903
 
2616
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.
2617
2905
  Findings are grouped per package, each registry package looked up once per run.
2618
-
2619
- 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;
2620
2907
  var init_audit = __esm(() => {
2621
2908
  init_src();
2622
2909
  init_advisories();
@@ -2624,146 +2911,95 @@ var init_audit = __esm(() => {
2624
2911
  init_config2();
2625
2912
  init_lockfile();
2626
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"]];
2627
2915
  options2 = {
2628
2916
  json: jsonOption("[{name, version, license, blocked, warnings, advisories}] for the packages with a finding"),
2629
2917
  "min-age": { type: "string", description: `days a version must be old (${DEFAULT_MIN_AGE_DAYS})` },
2630
- "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" },
2631
2920
  ignore: { type: "string", multiple: true, description: "an advisory skipped by GHSA or numeric id, repeatable" }
2632
2921
  };
2633
2922
  });
2634
2923
 
2635
- // ../kit/src/shell.ts
2636
- function wrapper(short = "", long = [], operands = 0) {
2637
- return { short, long, operands };
2638
- }
2639
- var SEPARATORS2, PLAIN_BREAKERS, GLOB_CHARS, COMMENT_PREFIXES, DOUBLE_QUOTED_ESCAPES, KEYWORDS, SHELLS, COMMAND_STRING_PROGRAMS, SHELL_VALUE_OPTIONS, PRINTERS, FIND_EXEC, WRAPPERS;
2640
- var init_shell = __esm(() => {
2641
- SEPARATORS2 = new Set([`
2642
- `, ";", "|"]);
2643
- PLAIN_BREAKERS = new Set(["<", ">", "(", ")", "&", "|"]);
2644
- GLOB_CHARS = new Set(["*", "?", "["]);
2645
- COMMENT_PREFIXES = new Set([";", "&", "|", "(", ")"]);
2646
- DOUBLE_QUOTED_ESCAPES = new Set(["$", "`", '"', "\\"]);
2647
- KEYWORDS = new Set(["then", "do", "else", "if", "elif", "while", "until", "!", "{", "}"]);
2648
- SHELLS = new Set(["sh", "bash", "zsh", "dash", "ash", "ksh"]);
2649
- COMMAND_STRING_PROGRAMS = new Set([...SHELLS, "su", "runuser"]);
2650
- SHELL_VALUE_OPTIONS = new Set(["-o", "-O", "--rcfile", "--init-file"]);
2651
- PRINTERS = new Set(["echo", "printf"]);
2652
- FIND_EXEC = new Set(["-exec", "-execdir", "-ok", "-okdir"]);
2653
- WRAPPERS = new Map([
2654
- ["command", wrapper()],
2655
- ["builtin", wrapper()],
2656
- ["nohup", wrapper()],
2657
- ["setsid", wrapper()],
2658
- ["busybox", wrapper()],
2659
- ["exec", wrapper("a")],
2660
- ["time", wrapper("fo", ["--format", "--output"])],
2661
- ["env", wrapper("uCS", ["--unset", "--chdir", "--split-string", "--argv0"])],
2662
- ["sudo", wrapper("ughpCTrtUDR", ["--user", "--group", "--host", "--prompt", "--close-from", "--command-timeout", "--role", "--type", "--other-user", "--chdir", "--chroot"])],
2663
- ["doas", wrapper("uC")],
2664
- ["timeout", wrapper("sk", ["--signal", "--kill-after"], 1)],
2665
- ["nice", wrapper("n", ["--adjustment"])],
2666
- ["ionice", wrapper("cnpPu", ["--class", "--classdata", "--pid", "--pgid", "--uid"])],
2667
- ["stdbuf", wrapper("ioe", ["--input", "--output", "--error"])],
2668
- ["chroot", wrapper("", ["--userspec", "--groups"], 1)],
2669
- ["xargs", wrapper("IndsEaPL", ["--max-args", "--max-procs", "--max-chars", "--delimiter", "--arg-file", "--eof", "--max-lines", "--process-slot-var"])]
2670
- ]);
2924
+ // src/commands/outdated.ts
2925
+ var exports_outdated = {};
2926
+ __export(exports_outdated, {
2927
+ outdatedCommand: () => outdatedCommand
2671
2928
  });
2672
-
2673
- // src/pin.ts
2674
- function actions(byAction) {
2675
- 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;
2676
2983
  }
2677
- var LOCKFILES, ACTIONS, OPTION_FLAGS, DIR_FLAGS, VALUE_FLAGS;
2678
- var init_pin = __esm(() => {
2679
- init_shell();
2680
- LOCKFILES = [
2681
- ["bun.lock", "bun"],
2682
- ["bun.lockb", "bun"],
2683
- ["pnpm-lock.yaml", "pnpm"],
2684
- ["yarn.lock", "yarn"],
2685
- ["package-lock.json", "npm"]
2686
- ];
2687
- ACTIONS = {
2688
- bun: actions({ install: ["install", "i"], add: ["add", "a"], remove: ["remove", "rm"], update: ["update"] }),
2689
- pnpm: actions({ install: ["install", "i"], add: ["add"], remove: ["remove", "rm"], update: ["update", "up"] }),
2690
- yarn: new Map([...actions({ install: ["", "install"], add: ["add"], remove: ["remove"], update: ["upgrade"] }), ["upgrade-interactive", null], ["import", null]]),
2691
- npm: actions({
2692
- install: ["install", "i", "in", "ins", "inst", "insta", "instal", "isnt", "isnta", "isntal", "isntall"],
2693
- add: ["add"],
2694
- frozen: ["ci", "clean-install", "ic", "install-clean", "isntall-clean"],
2695
- remove: ["uninstall", "unlink", "remove", "rm", "r", "un"],
2696
- update: ["update", "u", "up", "upgrade", "udpate"]
2697
- })
2698
- };
2699
- OPTION_FLAGS = new Map([
2700
- ["-D", "dev"],
2701
- ["--save-dev", "dev"],
2702
- ["--dev", "dev"],
2703
- ["-E", "exact"],
2704
- ["--save-exact", "exact"],
2705
- ["--exact", "exact"]
2706
- ]);
2707
- DIR_FLAGS = new Set(["--cwd", "-C", "--dir", "--prefix"]);
2708
- VALUE_FLAGS = new Set([...DIR_FLAGS, "--filter", "-F"]);
2709
- });
2984
+ var usage4 = `pkg outdated [dir]
2710
2985
 
2711
- // src/consumers.ts
2712
- import { existsSync, readdirSync, readFileSync } from "fs";
2713
- import { dirname as dirname3, join as join4, relative } from "path";
2714
- function packageFiles(dir, depth, found) {
2715
- let entries;
2716
- try {
2717
- entries = readdirSync(dir, { withFileTypes: true });
2718
- } catch {
2719
- return found;
2720
- }
2721
- for (const entry of entries) {
2722
- if (entry.isFile() && entry.name === "package.json")
2723
- found.push(join4(dir, entry.name));
2724
- else if (entry.isDirectory() && depth < MAX_DEPTH && !entry.name.startsWith(".") && !SKIPPED.has(entry.name))
2725
- packageFiles(join4(dir, entry.name), depth + 1, found);
2726
- }
2727
- return found;
2728
- }
2729
- function managerFor(dir, root) {
2730
- for (let at = dir;; at = dirname3(at)) {
2731
- const lockfile = LOCKFILES.find(([file]) => existsSync(join4(at, file)));
2732
- if (lockfile !== undefined)
2733
- return lockfile[1];
2734
- if (at === root || at === dirname3(at))
2735
- return "bun";
2736
- }
2737
- }
2738
- function findConsumers(name, root) {
2739
- const consumers = [];
2740
- for (const path of packageFiles(root, 0, []).sort()) {
2741
- let manifest;
2742
- try {
2743
- manifest = JSON.parse(readFileSync(path, "utf8"));
2744
- } catch {
2745
- continue;
2746
- }
2747
- const kind = KINDS.find((each) => typeof manifest[each]?.[name] === "string");
2748
- const spec = kind === undefined ? undefined : manifest[kind]?.[name];
2749
- if (kind !== undefined && spec !== undefined)
2750
- consumers.push({ dir: relative(root, dirname3(path)) || ".", path, kind, spec, manager: managerFor(dirname3(path), root) });
2751
- }
2752
- return consumers;
2753
- }
2754
- function isLocal(consumer) {
2755
- return LOCAL_SPEC.test(consumer.spec);
2756
- }
2757
- function addCommand(consumer, name, version) {
2758
- return ["bun", "add", ...KIND_FLAGS[consumer.kind], "--exact", "--no-cache", `${name}@${version}`];
2759
- }
2760
- var KINDS, SKIPPED, MAX_DEPTH = 5, LOCAL_SPEC, KIND_FLAGS;
2761
- var init_consumers = __esm(() => {
2762
- init_pin();
2763
- KINDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
2764
- SKIPPED = new Set(["node_modules", "dist", "build", "vendor", "target", "coverage"]);
2765
- LOCAL_SPEC = /[:/]/;
2766
- 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
+ };
2767
3003
  });
2768
3004
 
2769
3005
  // src/commands/bump.ts
@@ -2772,9 +3008,9 @@ __export(exports_bump, {
2772
3008
  ROOT_OPTION: () => ROOT_OPTION,
2773
3009
  bump: () => bump
2774
3010
  });
2775
- import { existsSync as existsSync2 } from "fs";
2776
- import { homedir as homedir3 } from "os";
2777
- import { dirname as dirname4, join as join5, 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";
2778
3014
  async function lookup(name, registry, fetcher) {
2779
3015
  try {
2780
3016
  return { packument: await fetchPackument(name, registry, fetcher) };
@@ -2792,7 +3028,7 @@ async function servedVersion(name, version, settings) {
2792
3028
  const registry = registryFor(name, settings.configs);
2793
3029
  const host = new URL(registry.url).host;
2794
3030
  const started = Date.now();
2795
- 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)}`]);
2796
3032
  let lastError = "";
2797
3033
  try {
2798
3034
  for (;; ) {
@@ -2811,7 +3047,7 @@ async function servedVersion(name, version, settings) {
2811
3047
  return version;
2812
3048
  }
2813
3049
  if (Date.now() - started >= settings.timeout)
2814
- 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}`}`);
2815
3051
  region.redraw();
2816
3052
  await Bun.sleep(settings.poll ?? POLL);
2817
3053
  }
@@ -2834,11 +3070,11 @@ async function vet(name, version, settings) {
2834
3070
  return false;
2835
3071
  }
2836
3072
  function lockfileOf(dir, root) {
2837
- for (let at = dir;; at = dirname4(at)) {
2838
- const found = ["bun.lock", "bun.lockb"].find((file) => existsSync2(join5(at, file)));
3073
+ for (let at = dir;; at = dirname5(at)) {
3074
+ const found = ["bun.lock", "bun.lockb"].find((file) => existsSync4(join8(at, file)));
2839
3075
  if (found !== undefined)
2840
- return relative2(dir, join5(at, found));
2841
- if (at === root || at === dirname4(at))
3076
+ return relative2(dir, join8(at, found));
3077
+ if (at === root || at === dirname5(at))
2842
3078
  return;
2843
3079
  }
2844
3080
  }
@@ -2872,7 +3108,7 @@ async function bumpConsumers(name, version, settings) {
2872
3108
  note(`${local} non-registry dependenc${local === 1 ? "y" : "ies"} (workspace:, file:, git, URL, alias, catalog:)`, "left as they are");
2873
3109
  for (const consumer of consumers) {
2874
3110
  const argv = addCommand(consumer, name, version);
2875
- const where = join5(settings.root, consumer.dir);
3111
+ const where = join8(settings.root, consumer.dir);
2876
3112
  if (consumer.kind === "peerDependencies") {
2877
3113
  skipped.push(consumer.dir);
2878
3114
  note(consumer.dir, `peer range ${consumer.spec}, left as it is`);
@@ -2915,9 +3151,9 @@ async function bumpConsumers(name, version, settings) {
2915
3151
  return isClean;
2916
3152
  }
2917
3153
  async function bump(args) {
2918
- const { values, positionals: [name, asked] } = parse(args, usage4, options3, { positionals: ["<name>", "[version]"] });
3154
+ const { values, positionals: [name, asked] } = parse(args, usage5, options4, { positionals: ["<name>", "[version]"] });
2919
3155
  const settings = {
2920
- root: values.root ?? join5(homedir3(), "Workspace"),
3156
+ root: values.root ?? join8(homedir4(), "Workspace"),
2921
3157
  configs: await loadConfigs(),
2922
3158
  timeout: duration(values.timeout, "timeout", 10 * 60000),
2923
3159
  isDryRun: values["dry-run"] === true,
@@ -2931,14 +3167,14 @@ async function bump(args) {
2931
3167
  return 1;
2932
3168
  return await bumpConsumers(name, version, settings) ? 0 : 1;
2933
3169
  }
2934
- var usage4 = `pkg bump <name> [version]
3170
+ var usage5 = `pkg bump <name> [version]
2935
3171
 
2936
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.
2937
3173
  A version the vetting blocks is refused before any folder is touched, unless --force is given.
2938
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.
2939
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.
2940
3176
 
2941
- 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;
2942
3178
  var init_bump = __esm(() => {
2943
3179
  init_src();
2944
3180
  init_progress();
@@ -2948,7 +3184,7 @@ var init_bump = __esm(() => {
2948
3184
  init_semver();
2949
3185
  init_check();
2950
3186
  ROOT_OPTION = { type: "string", description: "the folder holding the repositories (~/Workspace)" };
2951
- options3 = {
3187
+ options4 = {
2952
3188
  root: ROOT_OPTION,
2953
3189
  timeout: { type: "string", short: "t", description: "how long to wait for the registry (10m): 30s, 5m, 1h" },
2954
3190
  "all-consumers": { type: "boolean", description: "after each install run the folder's check and commit it through gc when it passes" },
@@ -2963,11 +3199,11 @@ var exports_consumers = {};
2963
3199
  __export(exports_consumers, {
2964
3200
  consumers: () => consumers
2965
3201
  });
2966
- import { homedir as homedir4 } from "os";
2967
- import { join as join6 } from "path";
3202
+ import { homedir as homedir5 } from "os";
3203
+ import { join as join9 } from "path";
2968
3204
  function consumers(args) {
2969
- const { values, positionals: [name] } = parse(args, usage5, options4, { positionals: ["<name>"] });
2970
- const root = values.root ?? join6(homedir4(), "Workspace");
3205
+ const { values, positionals: [name] } = parse(args, usage6, options5, { positionals: ["<name>"] });
3206
+ const root = values.root ?? join9(homedir5(), "Workspace");
2971
3207
  const { shown, footer } = capped(findConsumers(name, root), limitOf(values, CONSUMERS_LIMIT));
2972
3208
  if (values.json === true)
2973
3209
  printJson(shown);
@@ -2978,16 +3214,16 @@ function consumers(args) {
2978
3214
  `));
2979
3215
  return 0;
2980
3216
  }
2981
- var usage5 = `pkg consumers <name>
3217
+ var usage6 = `pkg consumers <name>
2982
3218
 
2983
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).
2984
3220
 
2985
- 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;
2986
3222
  var init_consumers2 = __esm(() => {
2987
3223
  init_src();
2988
3224
  init_consumers();
2989
3225
  init_bump();
2990
- options4 = {
3226
+ options5 = {
2991
3227
  root: ROOT_OPTION,
2992
3228
  ...limitOptions(CONSUMERS_LIMIT),
2993
3229
  json: jsonOption("[{dir, path, kind, spec, manager}]")
@@ -2996,14 +3232,9 @@ var init_consumers2 = __esm(() => {
2996
3232
 
2997
3233
  // src/cli.ts
2998
3234
  init_src();
2999
- import process12 from "process";
3000
- // package.json
3001
- var version = "0.7.1";
3002
-
3003
- // src/cli.ts
3004
- process12.exitCode = await runCli({
3235
+ import process15 from "process";
3236
+ process15.exitCode = await runCli({
3005
3237
  name: "pkg",
3006
- version,
3007
3238
  about: "checks npm packages before they are added, and moves a lib's consumers onto a new release",
3008
3239
  replaces: "bun add on trust, and a bun info and sleep loop then a bun add per consumer",
3009
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"]],
@@ -3011,6 +3242,7 @@ process12.exitCode = await runCli({
3011
3242
  commands: {
3012
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) },
3013
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) },
3014
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) },
3015
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) }
3016
3248
  }