@kaynooo/pkg-tools 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/cli.js +699 -317
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -418,6 +418,29 @@ var init_format = __esm(() => {
|
|
|
418
418
|
ESCAPE = String.fromCharCode(27);
|
|
419
419
|
});
|
|
420
420
|
|
|
421
|
+
// ../kit/src/text.ts
|
|
422
|
+
function distance(a, b) {
|
|
423
|
+
let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
424
|
+
for (const [i, left] of [...a].entries()) {
|
|
425
|
+
const current = [i + 1];
|
|
426
|
+
for (const [j, right] of [...b].entries())
|
|
427
|
+
current.push(Math.min((previous[j + 1] ?? 0) + 1, (current[j] ?? 0) + 1, (previous[j] ?? 0) + (left === right ? 0 : 1)));
|
|
428
|
+
previous = current;
|
|
429
|
+
}
|
|
430
|
+
return previous[b.length] ?? 0;
|
|
431
|
+
}
|
|
432
|
+
function closest(name, names) {
|
|
433
|
+
const started = names.find((known) => known.startsWith(name));
|
|
434
|
+
if (started !== undefined)
|
|
435
|
+
return started;
|
|
436
|
+
const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
|
|
437
|
+
return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
|
|
438
|
+
}
|
|
439
|
+
var ANSI;
|
|
440
|
+
var init_text = __esm(() => {
|
|
441
|
+
ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
|
|
442
|
+
});
|
|
443
|
+
|
|
421
444
|
// ../kit/src/args.ts
|
|
422
445
|
import process3 from "process";
|
|
423
446
|
import { parseArgs } from "util";
|
|
@@ -473,10 +496,10 @@ function optionRows(options, colors = paint(process3.stdout)) {
|
|
|
473
496
|
const rows = Object.entries(options).map(([name, option]) => {
|
|
474
497
|
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
475
498
|
const value = option.type === "string" ? ` ${colors.dim("<value>")}` : "";
|
|
476
|
-
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? " <value>".length : 0), description: option.description };
|
|
499
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? " <value>".length : 0), description: option.description ?? "" };
|
|
477
500
|
});
|
|
478
501
|
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
479
|
-
return rows.map((row) => ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
502
|
+
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
480
503
|
}
|
|
481
504
|
function applyOutput(color, plain, env = process3.env) {
|
|
482
505
|
const when = plain === true ? "never" : color ?? "auto";
|
|
@@ -504,7 +527,8 @@ function parse(args, usage, options, settings = {}) {
|
|
|
504
527
|
try {
|
|
505
528
|
parsed = parseArgs({ args: joinDashValues(own, all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
506
529
|
} catch (error) {
|
|
507
|
-
|
|
530
|
+
const message = parseError(error);
|
|
531
|
+
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
508
532
|
}
|
|
509
533
|
const values = parsed.values;
|
|
510
534
|
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
@@ -547,12 +571,27 @@ function parseError(error) {
|
|
|
547
571
|
const message = upTo(error instanceof Error ? error.message : String(error), ". ");
|
|
548
572
|
return `${message.charAt(0).toLowerCase()}${message.slice(1)}`;
|
|
549
573
|
}
|
|
574
|
+
function nearestOption(message, options, allowNegative) {
|
|
575
|
+
const typed = UNKNOWN_OPTION.exec(message)?.[1];
|
|
576
|
+
if (typed === undefined)
|
|
577
|
+
return;
|
|
578
|
+
const word = typed.replace(DASHES, "");
|
|
579
|
+
if (word.length < 2 && !typed.startsWith("--"))
|
|
580
|
+
return;
|
|
581
|
+
const known = Object.entries(options).flatMap(([name, option]) => [
|
|
582
|
+
...option.short === undefined ? [] : [`-${option.short}`],
|
|
583
|
+
`--${name}`,
|
|
584
|
+
...allowNegative && option.type === "boolean" ? [`--no-${name}`] : []
|
|
585
|
+
]);
|
|
586
|
+
const meant = closest(word, known.map((flag) => flag.replace(DASHES, "")));
|
|
587
|
+
return meant === undefined ? undefined : `did you mean ${known.find((flag) => flag.replace(DASHES, "") === meant)}?`;
|
|
588
|
+
}
|
|
550
589
|
function wholeNumber(value, name, fallback, min = 1) {
|
|
551
590
|
if (value === undefined)
|
|
552
591
|
return fallback;
|
|
553
592
|
const number = Number(value);
|
|
554
|
-
if (!Number.
|
|
555
|
-
badUsage(`--${name} takes a whole number from ${min}, not ${value}`);
|
|
593
|
+
if (!WHOLE.test(value) || !Number.isSafeInteger(number) || number < min)
|
|
594
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes a whole number from ${min}, not ${value}`);
|
|
556
595
|
return number;
|
|
557
596
|
}
|
|
558
597
|
function duration(value, name, fallback, unit = "s") {
|
|
@@ -564,10 +603,11 @@ function duration(value, name, fallback, unit = "s") {
|
|
|
564
603
|
badUsage(`--${name} takes a duration such as 30s, 5m or 1h, not ${value}`);
|
|
565
604
|
return ms;
|
|
566
605
|
}
|
|
567
|
-
var Exit, UsageError, HelpShown, completion, OPTIONAL, USAGE_PREFIX, FULL_STOP, OUTPUT_OPTIONS, DURATION, UNIT_MS;
|
|
606
|
+
var Exit, UsageError, HelpShown, completion, OPTIONAL, USAGE_PREFIX, FULL_STOP, DASHES, OUTPUT_OPTIONS, UNKNOWN_OPTION, WHOLE, DURATION, UNIT_MS;
|
|
568
607
|
var init_args = __esm(() => {
|
|
569
608
|
init_format();
|
|
570
609
|
init_style();
|
|
610
|
+
init_text();
|
|
571
611
|
Exit = class Exit extends Error {
|
|
572
612
|
code;
|
|
573
613
|
hint;
|
|
@@ -578,8 +618,8 @@ var init_args = __esm(() => {
|
|
|
578
618
|
}
|
|
579
619
|
};
|
|
580
620
|
UsageError = class UsageError extends Exit {
|
|
581
|
-
constructor(message) {
|
|
582
|
-
super(message, 2);
|
|
621
|
+
constructor(message, hint) {
|
|
622
|
+
super(message, 2, hint);
|
|
583
623
|
}
|
|
584
624
|
};
|
|
585
625
|
HelpShown = class HelpShown extends Exit {
|
|
@@ -591,10 +631,13 @@ var init_args = __esm(() => {
|
|
|
591
631
|
OPTIONAL = /\[[^\]]+\]/g;
|
|
592
632
|
USAGE_PREFIX = /^usage:\s*/i;
|
|
593
633
|
FULL_STOP = /\.$/;
|
|
634
|
+
DASHES = /^-+/;
|
|
594
635
|
OUTPUT_OPTIONS = {
|
|
595
636
|
color: { type: "string", description: "auto (on a terminal alone), always or never" },
|
|
596
637
|
plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
|
|
597
638
|
};
|
|
639
|
+
UNKNOWN_OPTION = /^unknown option '(-[^']+)'/;
|
|
640
|
+
WHOLE = /^\d+$/;
|
|
598
641
|
DURATION = /^(\d+(?:\.\d+)?)(ms|[smh])?$/;
|
|
599
642
|
UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000 };
|
|
600
643
|
});
|
|
@@ -605,31 +648,265 @@ var init_chart = __esm(() => {
|
|
|
605
648
|
init_style();
|
|
606
649
|
});
|
|
607
650
|
|
|
608
|
-
// ../kit/src/
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
651
|
+
// ../kit/src/process.ts
|
|
652
|
+
import { constants } from "os";
|
|
653
|
+
import process4 from "process";
|
|
654
|
+
function exitCodeOf(code, signal) {
|
|
655
|
+
if (code !== null)
|
|
656
|
+
return code;
|
|
657
|
+
if (typeof signal === "number")
|
|
658
|
+
return 128 + signal;
|
|
659
|
+
return signal === null ? 129 : 128 + constants.signals[signal];
|
|
660
|
+
}
|
|
661
|
+
function holdSignals(cleanup) {
|
|
662
|
+
let received;
|
|
663
|
+
let child;
|
|
664
|
+
const held = {
|
|
665
|
+
get signal() {
|
|
666
|
+
return received;
|
|
667
|
+
},
|
|
668
|
+
forward: (to) => {
|
|
669
|
+
child = to;
|
|
670
|
+
},
|
|
671
|
+
release: () => {
|
|
672
|
+
for (const signal of SIGNALS)
|
|
673
|
+
process4.off(signal, onSignal);
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
function onSignal(signal) {
|
|
677
|
+
received = signal;
|
|
678
|
+
if (child !== undefined) {
|
|
679
|
+
child.kill(signal);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (cleanup === undefined)
|
|
683
|
+
return;
|
|
684
|
+
held.release();
|
|
685
|
+
cleanup(signal);
|
|
686
|
+
process4.kill(process4.pid, signal);
|
|
616
687
|
}
|
|
617
|
-
|
|
688
|
+
for (const signal of SIGNALS)
|
|
689
|
+
process4.on(signal, onSignal);
|
|
690
|
+
return held;
|
|
618
691
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
692
|
+
async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal, maxBytes = Infinity, onChunk, onStart } = {}) {
|
|
693
|
+
const chunks = [];
|
|
694
|
+
const grouped = timeoutMs !== undefined || signal !== undefined || onStart !== undefined || onChunk !== undefined;
|
|
695
|
+
const proc = Bun.spawn(argv, { ...cwd === undefined ? {} : { cwd }, ...env === undefined ? {} : { env }, stdin, stdout: "pipe", stderr: "pipe", ...grouped ? { detached: true } : {} });
|
|
696
|
+
const state = { kept: 0, truncated: false, timedOut: false, failure: undefined, done: false };
|
|
697
|
+
const timers = [];
|
|
698
|
+
const killGroup = (killing) => {
|
|
699
|
+
if (state.done)
|
|
700
|
+
return;
|
|
701
|
+
try {
|
|
702
|
+
if (grouped)
|
|
703
|
+
process4.kill(-proc.pid, killing);
|
|
704
|
+
else
|
|
705
|
+
proc.kill(killing);
|
|
706
|
+
} catch {}
|
|
707
|
+
};
|
|
708
|
+
const stop = () => {
|
|
709
|
+
killGroup("SIGTERM");
|
|
710
|
+
timers.push(setTimeout(killGroup, KILL_GRACE_MS, "SIGKILL"));
|
|
711
|
+
};
|
|
712
|
+
const onAbort = () => stop();
|
|
713
|
+
if (timeoutMs !== undefined) {
|
|
714
|
+
timers.push(setTimeout(() => {
|
|
715
|
+
state.timedOut = true;
|
|
716
|
+
stop();
|
|
717
|
+
}, timeoutMs));
|
|
718
|
+
}
|
|
719
|
+
if (signal?.aborted === true)
|
|
720
|
+
onAbort();
|
|
721
|
+
else
|
|
722
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
723
|
+
onStart?.({ kill: (killing) => killGroup(killing) });
|
|
724
|
+
const drain = async (stream, name) => {
|
|
725
|
+
for await (const data of stream) {
|
|
726
|
+
if (state.failure !== undefined)
|
|
727
|
+
continue;
|
|
728
|
+
try {
|
|
729
|
+
onChunk?.({ stream: name, data });
|
|
730
|
+
} catch (error) {
|
|
731
|
+
state.failure = error;
|
|
732
|
+
stop();
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
if (state.kept + data.length > maxBytes) {
|
|
736
|
+
state.truncated = true;
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
state.kept += data.length;
|
|
740
|
+
chunks.push({ stream: name, data });
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
try {
|
|
744
|
+
await Promise.all([drain(proc.stdout, "stdout"), drain(proc.stderr, "stderr")]);
|
|
745
|
+
await proc.exited;
|
|
746
|
+
} finally {
|
|
747
|
+
state.done = true;
|
|
748
|
+
for (const timer of timers)
|
|
749
|
+
clearTimeout(timer);
|
|
750
|
+
signal?.removeEventListener("abort", onAbort);
|
|
751
|
+
}
|
|
752
|
+
if (state.failure !== undefined)
|
|
753
|
+
throw state.failure;
|
|
754
|
+
const joined = () => Buffer.concat(chunks.map(({ data }) => data));
|
|
755
|
+
let output;
|
|
756
|
+
let text;
|
|
757
|
+
return {
|
|
758
|
+
code: exitCodeOf(proc.exitCode, proc.signalCode),
|
|
759
|
+
chunks,
|
|
760
|
+
timedOut: state.timedOut,
|
|
761
|
+
truncated: state.truncated,
|
|
762
|
+
get output() {
|
|
763
|
+
return output ??= joined();
|
|
764
|
+
},
|
|
765
|
+
get text() {
|
|
766
|
+
return text ??= (output ??= joined()).toString();
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
var KILL_GRACE_MS = 2000, SIGNALS;
|
|
771
|
+
var init_process = __esm(() => {
|
|
772
|
+
SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// ../kit/src/progress.ts
|
|
776
|
+
import process5 from "process";
|
|
777
|
+
function liveRegion(render, stream = process5.stderr, env = process5.env) {
|
|
778
|
+
if (!takesControlCodes(stream, env))
|
|
779
|
+
return { redraw: () => {
|
|
780
|
+
return;
|
|
781
|
+
}, print: (text) => void stream.write(`${text}
|
|
782
|
+
`), stop: () => {
|
|
783
|
+
return;
|
|
784
|
+
} };
|
|
785
|
+
const write = stream.write.bind(stream);
|
|
786
|
+
let frame = 0;
|
|
787
|
+
let drawn = 0;
|
|
788
|
+
const clear = () => {
|
|
789
|
+
if (drawn > 0)
|
|
790
|
+
write(`${CLEAR_LINE}${LINE_UP.repeat(drawn - 1)}`);
|
|
791
|
+
drawn = 0;
|
|
792
|
+
};
|
|
793
|
+
const draw = () => {
|
|
794
|
+
const width = (stream.columns === undefined || stream.columns === 0 ? 80 : stream.columns) - 1;
|
|
795
|
+
const height = stream.rows === undefined || stream.rows === 0 ? 24 : stream.rows;
|
|
796
|
+
const lines = render(FRAMES[frame++ % FRAMES.length]).slice(0, Math.max(1, height - 1)).map((line) => cutToWidth(line, width));
|
|
797
|
+
if (lines.length === 0) {
|
|
798
|
+
clear();
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
write(`${drawn > 0 ? `${CLEAR_LINE}${LINE_UP.repeat(drawn - 1)}` : CLEAR_LINE}${lines.join(`
|
|
802
|
+
`)}`);
|
|
803
|
+
drawn = lines.length;
|
|
804
|
+
};
|
|
805
|
+
const timer = setInterval(draw, FRAME_MS);
|
|
806
|
+
const restore = interceptOutput(clear);
|
|
807
|
+
const onExit = () => clear();
|
|
808
|
+
const signals = holdSignals(stop);
|
|
809
|
+
function stop() {
|
|
810
|
+
clearInterval(timer);
|
|
811
|
+
clear();
|
|
812
|
+
restore();
|
|
813
|
+
process5.off("exit", onExit);
|
|
814
|
+
signals.release();
|
|
815
|
+
}
|
|
816
|
+
timer.unref();
|
|
817
|
+
process5.on("exit", onExit);
|
|
818
|
+
return {
|
|
819
|
+
redraw: draw,
|
|
820
|
+
print: (text) => {
|
|
821
|
+
clear();
|
|
822
|
+
write(`${text}
|
|
823
|
+
`);
|
|
824
|
+
draw();
|
|
825
|
+
},
|
|
826
|
+
stop
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function cutToWidth(line, width) {
|
|
830
|
+
if (Bun.stringWidth(line) <= width)
|
|
831
|
+
return line;
|
|
832
|
+
let room = width;
|
|
833
|
+
let kept = "";
|
|
834
|
+
let index = 0;
|
|
835
|
+
let linked = false;
|
|
836
|
+
while (index < line.length) {
|
|
837
|
+
ESCAPE_CODE.lastIndex = index;
|
|
838
|
+
const escape = ESCAPE_CODE.exec(line);
|
|
839
|
+
if (escape !== null) {
|
|
840
|
+
kept += escape[0];
|
|
841
|
+
linked ||= escape[0].startsWith(`${ESC}]`);
|
|
842
|
+
index = ESCAPE_CODE.lastIndex;
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
const char = String.fromCodePoint(line.codePointAt(index));
|
|
846
|
+
const columns = Bun.stringWidth(char);
|
|
847
|
+
if (columns > room)
|
|
848
|
+
break;
|
|
849
|
+
kept += char;
|
|
850
|
+
room -= columns;
|
|
851
|
+
index += char.length;
|
|
852
|
+
}
|
|
853
|
+
return `${kept}${RESET_STYLE}${linked ? CLOSE_LINK : ""}`;
|
|
854
|
+
}
|
|
855
|
+
function interceptOutput(clear) {
|
|
856
|
+
const targets = [...CONSOLE_METHODS.map((name) => [console, name]), [process5.stdout, "write"], [process5.stderr, "write"]];
|
|
857
|
+
const wrapped = targets.map(([target, name]) => {
|
|
858
|
+
const original = Reflect.get(target, name);
|
|
859
|
+
const wrapper = (...args) => {
|
|
860
|
+
clear();
|
|
861
|
+
return original.apply(target, args);
|
|
862
|
+
};
|
|
863
|
+
Reflect.set(target, name, wrapper);
|
|
864
|
+
return { target, name, original, wrapper };
|
|
865
|
+
});
|
|
866
|
+
return () => {
|
|
867
|
+
for (const { target, name, original, wrapper } of wrapped) {
|
|
868
|
+
if (Reflect.get(target, name) === wrapper)
|
|
869
|
+
Reflect.set(target, name, original);
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
var FRAMES, FRAME_MS = 80, CLEAR_LINE = "\r\x1B[2K", LINE_UP = "\x1B[1A\x1B[2K", ESC, ESCAPE_CODE, RESET_STYLE, CLOSE_LINK, CONSOLE_METHODS;
|
|
874
|
+
var init_progress = __esm(() => {
|
|
875
|
+
init_format();
|
|
876
|
+
init_process();
|
|
877
|
+
init_style();
|
|
878
|
+
FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
879
|
+
ESC = String.fromCharCode(27);
|
|
880
|
+
ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x7E]|\\][^\\u0007${ESC}]*(?:\\u0007|${ESC}\\\\))`, "y");
|
|
881
|
+
RESET_STYLE = `${ESC}[0m`;
|
|
882
|
+
CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
883
|
+
CONSOLE_METHODS = ["log", "info", "warn", "error", "debug", "dir", "dirxml", "table", "trace", "group", "groupCollapsed", "count", "timeEnd", "timeLog", "assert"];
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// ../kit/src/tool.ts
|
|
887
|
+
var init_tool = __esm(() => {
|
|
888
|
+
init_format();
|
|
889
|
+
init_process();
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
// ../kit/src/ci.ts
|
|
893
|
+
var init_ci = __esm(() => {
|
|
894
|
+
init_args();
|
|
895
|
+
init_format();
|
|
896
|
+
init_progress();
|
|
897
|
+
init_tool();
|
|
622
898
|
});
|
|
623
899
|
|
|
624
900
|
// ../kit/src/cli.ts
|
|
625
|
-
import
|
|
626
|
-
function commandRows(commands, colors) {
|
|
901
|
+
import process6 from "process";
|
|
902
|
+
async function commandRows(commands, colors) {
|
|
627
903
|
const width = Math.max(...Object.keys(commands).map((name) => name.length));
|
|
628
904
|
const groups = new Map([["Commands:", []]]);
|
|
629
|
-
|
|
905
|
+
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
906
|
+
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
630
907
|
const heading = group === undefined ? "Commands:" : `${group}:`;
|
|
631
908
|
const rows = groups.get(heading) ?? [];
|
|
632
|
-
rows.push(` ${colors.cyan(name.padEnd(width))} ${colors.dim(
|
|
909
|
+
rows.push(` ${colors.cyan(name.padEnd(width))} ${colors.dim(summaries[index])}`);
|
|
633
910
|
groups.set(heading, rows);
|
|
634
911
|
}
|
|
635
912
|
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
@@ -649,28 +926,38 @@ function takeOutputFlags(argv) {
|
|
|
649
926
|
}
|
|
650
927
|
return argv.slice(index);
|
|
651
928
|
}
|
|
652
|
-
function
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
929
|
+
async function resolveSummary(summary) {
|
|
930
|
+
return typeof summary === "string" ? summary : summary();
|
|
931
|
+
}
|
|
932
|
+
function scopeOf(commands, extra, aliases) {
|
|
933
|
+
return { commands: typeof commands === "function" ? {} : commands, lazy: [...typeof commands === "function" ? [commands] : [], ...extra === undefined ? [] : [extra]], aliases };
|
|
934
|
+
}
|
|
935
|
+
async function expand(scope, words) {
|
|
936
|
+
if (scope.lazy.length === 0)
|
|
937
|
+
return scope;
|
|
938
|
+
const commands = { ...scope.commands };
|
|
939
|
+
for (const added of await Promise.all(scope.lazy.map(async (lazy) => lazy(words)))) {
|
|
940
|
+
for (const [name, command] of Object.entries(added))
|
|
941
|
+
commands[name] ??= command;
|
|
942
|
+
}
|
|
943
|
+
return { ...scope, commands, lazy: [] };
|
|
658
944
|
}
|
|
659
|
-
async function help({ path, header, summary, commands, defaultCommand, examples, replaces }, stream) {
|
|
945
|
+
async function help({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
|
|
660
946
|
const colors = paint(stream);
|
|
661
947
|
const prefix = path.join(" ");
|
|
948
|
+
const summary = await resolveSummary(lazySummary);
|
|
662
949
|
if (wantsHelpJson())
|
|
663
950
|
return JSON.stringify(description(`${prefix} <command> [options]: ${summary}`, { examples, replaces }));
|
|
664
951
|
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
665
952
|
const options = fallback !== undefined && "run" in fallback ? await optionsOf(fallback.run) : undefined;
|
|
666
953
|
const own = Object.entries(options ?? {}).filter(([name]) => !(name in TOP_OPTIONS));
|
|
667
954
|
return [
|
|
668
|
-
header(colors),
|
|
955
|
+
header(colors, summary),
|
|
669
956
|
"",
|
|
670
957
|
usageLine(`${prefix} ${defaultCommand === undefined ? colors.cyan("<command>") : `[${colors.cyan("<command>")}]`} [options]`, colors),
|
|
671
958
|
...replacesRows(replaces, colors),
|
|
672
959
|
"",
|
|
673
|
-
...commandRows(commands, colors),
|
|
960
|
+
...await commandRows(commands, colors),
|
|
674
961
|
...own.length === 0 ? [] : ["", `${colors.bold(`Options of ${defaultCommand}:`)} ${colors.dim("the command run when none is named")}`, ...optionRows(Object.fromEntries(own), colors)],
|
|
675
962
|
"",
|
|
676
963
|
colors.bold("Options:"),
|
|
@@ -682,8 +969,8 @@ async function help({ path, header, summary, commands, defaultCommand, examples,
|
|
|
682
969
|
`);
|
|
683
970
|
}
|
|
684
971
|
function printError(prefix, message, hint, cause) {
|
|
685
|
-
const colors = paint(
|
|
686
|
-
const styled = takesControlCodes(
|
|
972
|
+
const colors = paint(process6.stderr);
|
|
973
|
+
const styled = takesControlCodes(process6.stderr);
|
|
687
974
|
const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
|
|
688
975
|
const under = (label, text, tint) => text === undefined ? "" : `
|
|
689
976
|
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
@@ -716,11 +1003,11 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
716
1003
|
}
|
|
717
1004
|
const [name, ...args] = argv;
|
|
718
1005
|
if (name === undefined && level.path.length > 1) {
|
|
719
|
-
console.error(await help(level,
|
|
1006
|
+
console.error(await help(await expand(level, [...level.path.slice(1), ...argv]), process6.stderr));
|
|
720
1007
|
return 2;
|
|
721
1008
|
}
|
|
722
1009
|
if (name === undefined || name === "help" || name === "--help" || name === "-h") {
|
|
723
|
-
console.log(await help(level,
|
|
1010
|
+
console.log(await help(await expand(level, [...level.path.slice(1), ...argv]), process6.stdout));
|
|
724
1011
|
return 0;
|
|
725
1012
|
}
|
|
726
1013
|
if (level.path.length === 1 && (name === "--version" || name === "-v")) {
|
|
@@ -729,6 +1016,10 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
729
1016
|
}
|
|
730
1017
|
let command = level.commands[level.aliases?.[name] ?? name];
|
|
731
1018
|
let rest = args;
|
|
1019
|
+
if (command === undefined && level.lazy.length > 0) {
|
|
1020
|
+
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
1021
|
+
command = level.commands[level.aliases?.[name] ?? name];
|
|
1022
|
+
}
|
|
732
1023
|
if (command === undefined && level.defaultCommand !== undefined) {
|
|
733
1024
|
command = level.commands[level.defaultCommand];
|
|
734
1025
|
rest = argv;
|
|
@@ -740,7 +1031,7 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
740
1031
|
}
|
|
741
1032
|
if ("commands" in command) {
|
|
742
1033
|
const { summary } = command;
|
|
743
|
-
return dispatch({ path: [...level.path, name], header: (colors) => `${colors.bold(`${prefix} ${name}`)} ${
|
|
1034
|
+
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 });
|
|
744
1035
|
}
|
|
745
1036
|
try {
|
|
746
1037
|
return await command.run(rest);
|
|
@@ -763,28 +1054,34 @@ async function optionsOf(run) {
|
|
|
763
1054
|
async function leafCandidates(run, words) {
|
|
764
1055
|
if (!(words.at(-1) ?? "").startsWith("-"))
|
|
765
1056
|
return [FILES];
|
|
766
|
-
return Object.entries(await optionsOf(run)).map(([name, option]) => `--${name} ${option.description}`);
|
|
1057
|
+
return Object.entries(await optionsOf(run)).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
767
1058
|
}
|
|
768
|
-
async function candidates(
|
|
1059
|
+
async function candidates(scope, defaultCommand, words, before = []) {
|
|
769
1060
|
const [word = "", ...rest] = words;
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
1061
|
+
const listing = rest.length === 0 && !word.startsWith("-");
|
|
1062
|
+
let { commands, aliases } = scope;
|
|
1063
|
+
let command = commands[aliases?.[word] ?? word];
|
|
1064
|
+
if (listing || command === undefined) {
|
|
1065
|
+
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
1066
|
+
command = commands[aliases?.[word] ?? word];
|
|
1067
|
+
}
|
|
1068
|
+
if (listing)
|
|
1069
|
+
return Promise.all(Object.entries(commands).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
773
1070
|
if (command === undefined) {
|
|
774
1071
|
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
775
1072
|
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
776
1073
|
}
|
|
777
|
-
return "commands" in command ? candidates(command.commands, command.aliases, undefined, rest) : leafCandidates(command.run, rest);
|
|
1074
|
+
return "commands" in command ? candidates(scopeOf(command.commands, command.extraCommands, command.aliases), undefined, rest, [...before, word]) : leafCandidates(command.run, rest);
|
|
778
1075
|
}
|
|
779
1076
|
async function printCandidates(found) {
|
|
780
1077
|
for (const line of await found)
|
|
781
1078
|
console.log(line);
|
|
782
1079
|
return 0;
|
|
783
1080
|
}
|
|
784
|
-
async function runCli(cli, argv =
|
|
1081
|
+
async function runCli(cli, argv = process6.argv.slice(2)) {
|
|
785
1082
|
if (argv[0] === COMPLETE)
|
|
786
|
-
return printCandidates(candidates(cli.commands, cli.aliases, cli.defaultCommand, argv.slice(1)));
|
|
787
|
-
return dispatch({ path: [cli.name], header: (colors) => `${colors.bold(cli.name)} ${colors.dim(cli.version)} ${
|
|
1083
|
+
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
1084
|
+
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 });
|
|
788
1085
|
}
|
|
789
1086
|
var LEVEL_OPTIONS, TOP_OPTIONS, COMPLETE = "__complete", FILES = "__files__";
|
|
790
1087
|
var init_cli = __esm(() => {
|
|
@@ -803,6 +1100,12 @@ var init_config = __esm(() => {
|
|
|
803
1100
|
init_args();
|
|
804
1101
|
});
|
|
805
1102
|
|
|
1103
|
+
// ../kit/src/git.ts
|
|
1104
|
+
var init_git = __esm(() => {
|
|
1105
|
+
init_args();
|
|
1106
|
+
init_tool();
|
|
1107
|
+
});
|
|
1108
|
+
|
|
806
1109
|
// ../kit/src/limit.ts
|
|
807
1110
|
function limitOptions(fallback) {
|
|
808
1111
|
return {
|
|
@@ -828,114 +1131,105 @@ var init_limit = __esm(() => {
|
|
|
828
1131
|
init_args();
|
|
829
1132
|
});
|
|
830
1133
|
|
|
831
|
-
// ../kit/src/
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
get signal() {
|
|
850
|
-
return received;
|
|
851
|
-
},
|
|
852
|
-
forward: (to) => {
|
|
853
|
-
child = to;
|
|
854
|
-
},
|
|
855
|
-
release: () => {
|
|
856
|
-
for (const signal of SIGNALS)
|
|
857
|
-
process5.off(signal, onSignal);
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
function onSignal(signal) {
|
|
861
|
-
received = signal;
|
|
862
|
-
if (child !== undefined) {
|
|
863
|
-
child.kill(signal);
|
|
864
|
-
return;
|
|
865
|
-
}
|
|
866
|
-
if (cleanup === undefined)
|
|
867
|
-
return;
|
|
868
|
-
held.release();
|
|
869
|
-
cleanup(signal);
|
|
870
|
-
process5.kill(process5.pid, signal);
|
|
871
|
-
}
|
|
872
|
-
for (const signal of SIGNALS)
|
|
873
|
-
process5.on(signal, onSignal);
|
|
874
|
-
return held;
|
|
875
|
-
}
|
|
876
|
-
async function runCommand(argv, { cwd, env, stdin = "ignore" } = {}) {
|
|
877
|
-
const chunks = [];
|
|
878
|
-
const proc = Bun.spawn(argv, { ...cwd === undefined ? {} : { cwd }, ...env === undefined ? {} : { env }, stdin, stdout: "pipe", stderr: "pipe" });
|
|
879
|
-
await Promise.all([collect(proc.stdout, "stdout", chunks), collect(proc.stderr, "stderr", chunks)]);
|
|
880
|
-
await proc.exited;
|
|
881
|
-
const output = Buffer.concat(chunks.map(({ data }) => data));
|
|
882
|
-
return { code: exitCodeOf(proc.exitCode, proc.signalCode), chunks, output, text: output.toString() };
|
|
883
|
-
}
|
|
884
|
-
var SIGNALS;
|
|
885
|
-
var init_process = __esm(() => {
|
|
886
|
-
SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
1134
|
+
// ../kit/src/logs.ts
|
|
1135
|
+
var DAY_MS;
|
|
1136
|
+
var init_logs = __esm(() => {
|
|
1137
|
+
DAY_MS = 24 * 60 * 60 * 1000;
|
|
1138
|
+
});
|
|
1139
|
+
|
|
1140
|
+
// ../kit/src/secrets.ts
|
|
1141
|
+
var SECRET_WORDS;
|
|
1142
|
+
var init_secrets = __esm(() => {
|
|
1143
|
+
SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
// ../kit/src/sessions.ts
|
|
1147
|
+
var {Glob } = globalThis.Bun;
|
|
1148
|
+
var SESSION_FILES, ACCOUNT_DIRS;
|
|
1149
|
+
var init_sessions = __esm(() => {
|
|
1150
|
+
SESSION_FILES = new Glob("*.json");
|
|
1151
|
+
ACCOUNT_DIRS = new Glob("*/sessions");
|
|
887
1152
|
});
|
|
888
1153
|
|
|
889
1154
|
// ../kit/src/index.ts
|
|
890
1155
|
var init_src = __esm(() => {
|
|
891
1156
|
init_args();
|
|
892
1157
|
init_chart();
|
|
1158
|
+
init_ci();
|
|
893
1159
|
init_cli();
|
|
894
1160
|
init_config();
|
|
895
1161
|
init_format();
|
|
1162
|
+
init_git();
|
|
896
1163
|
init_limit();
|
|
1164
|
+
init_logs();
|
|
897
1165
|
init_process();
|
|
1166
|
+
init_secrets();
|
|
1167
|
+
init_sessions();
|
|
898
1168
|
init_style();
|
|
899
1169
|
init_text();
|
|
1170
|
+
init_tool();
|
|
900
1171
|
});
|
|
901
1172
|
|
|
902
1173
|
// src/registry.ts
|
|
903
1174
|
function isPublic(registry) {
|
|
904
1175
|
return registry.url.replace(TRAILING_SLASHES, "") === PUBLIC_REGISTRY;
|
|
905
1176
|
}
|
|
1177
|
+
function unreachable(message) {
|
|
1178
|
+
return fail(`could not check: ${message}`, 2);
|
|
1179
|
+
}
|
|
1180
|
+
function requestInit(token) {
|
|
1181
|
+
return { headers: { accept: "application/json", ...token === undefined ? {} : { authorization: `Bearer ${token}` } }, signal: AbortSignal.timeout(REQUEST_TIMEOUT) };
|
|
1182
|
+
}
|
|
906
1183
|
async function get(fetcher, url, token) {
|
|
907
1184
|
try {
|
|
908
|
-
return await fetcher(url,
|
|
1185
|
+
return await fetcher(url, requestInit(token));
|
|
909
1186
|
} catch (error) {
|
|
910
|
-
return
|
|
1187
|
+
return unreachable(`cannot reach ${new URL(url).host}: ${error instanceof Error ? error.message : String(error)}`);
|
|
911
1188
|
}
|
|
912
1189
|
}
|
|
1190
|
+
function isValidName(name) {
|
|
1191
|
+
return PACKAGE_NAME.test(name) && name.length <= 214;
|
|
1192
|
+
}
|
|
913
1193
|
async function fetchPackument(name, registry, fetcher = fetch) {
|
|
914
|
-
|
|
1194
|
+
if (!isValidName(name))
|
|
1195
|
+
fail(`${name} is not a valid npm package name`);
|
|
1196
|
+
const response = await get(fetcher, `${registry.url.replace(TRAILING_SLASHES, "")}/${encodeURIComponent(name).replace("%40", "@").replace("%2F", "%2f")}`, registry.token);
|
|
915
1197
|
if (response.status === 404)
|
|
916
1198
|
return;
|
|
917
1199
|
if (!response.ok)
|
|
918
|
-
|
|
919
|
-
|
|
1200
|
+
unreachable(`${new URL(registry.url).host} answered ${response.status} for ${name}`);
|
|
1201
|
+
let packument;
|
|
1202
|
+
try {
|
|
1203
|
+
packument = await response.json();
|
|
1204
|
+
} catch {
|
|
1205
|
+
return unreachable(`${new URL(registry.url).host} answered ${name} with something that is not JSON`);
|
|
1206
|
+
}
|
|
1207
|
+
if (packument?.name !== name)
|
|
1208
|
+
unreachable(`${new URL(registry.url).host} answered ${name} with the metadata of ${String(packument?.name)}`);
|
|
1209
|
+
return packument;
|
|
920
1210
|
}
|
|
921
1211
|
async function fetchDownloads(name, fetcher = fetch) {
|
|
922
|
-
const response = await get(fetcher, `${DOWNLOADS_API}/${name}`);
|
|
1212
|
+
const response = await get(fetcher, `${DOWNLOADS_API}/${encodeURIComponent(name)}`);
|
|
923
1213
|
if (!response.ok)
|
|
924
1214
|
return;
|
|
925
|
-
|
|
926
|
-
|
|
1215
|
+
try {
|
|
1216
|
+
return (await response.json()).downloads;
|
|
1217
|
+
} catch {
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
927
1220
|
}
|
|
928
|
-
var PUBLIC_REGISTRY = "https://registry.npmjs.org", DOWNLOADS_API = "https://api.npmjs.org/downloads/point/last-week", TRAILING_SLASHES;
|
|
1221
|
+
var PUBLIC_REGISTRY = "https://registry.npmjs.org", DOWNLOADS_API = "https://api.npmjs.org/downloads/point/last-week", TRAILING_SLASHES, REQUEST_TIMEOUT = 1e4, PACKAGE_NAME;
|
|
929
1222
|
var init_registry = __esm(() => {
|
|
930
1223
|
init_src();
|
|
931
1224
|
TRAILING_SLASHES = /\/+$/;
|
|
1225
|
+
PACKAGE_NAME = /^(?:@[\w~-][\w.~-]*\/)?[\w~-][\w.~-]*$/;
|
|
932
1226
|
});
|
|
933
1227
|
|
|
934
1228
|
// src/config.ts
|
|
935
1229
|
import { homedir } from "os";
|
|
936
1230
|
import { dirname, join } from "path";
|
|
937
|
-
import
|
|
938
|
-
function
|
|
1231
|
+
import process8 from "process";
|
|
1232
|
+
function expand2(value, env) {
|
|
939
1233
|
let missing = false;
|
|
940
1234
|
const expanded = value.replace(ENV_REFERENCE, (_, braced, bare) => {
|
|
941
1235
|
const found = env[braced ?? bare ?? ""];
|
|
@@ -944,6 +1238,9 @@ function expand(value, env) {
|
|
|
944
1238
|
});
|
|
945
1239
|
return missing ? undefined : expanded;
|
|
946
1240
|
}
|
|
1241
|
+
function literal(value) {
|
|
1242
|
+
return expand2(value, {}) === value ? value : undefined;
|
|
1243
|
+
}
|
|
947
1244
|
function parseNpmrc(text, env) {
|
|
948
1245
|
const config = { scopes: {}, tokens: {}, scopeTokens: {} };
|
|
949
1246
|
for (const line of text.split(`
|
|
@@ -952,11 +1249,12 @@ function parseNpmrc(text, env) {
|
|
|
952
1249
|
if (at === -1 || COMMENT.test(line))
|
|
953
1250
|
continue;
|
|
954
1251
|
const key = line.slice(0, at).trim();
|
|
955
|
-
const value = expand(line.slice(at + 1).trim().replace(QUOTES, "$2"), env);
|
|
956
|
-
if (value === undefined || value === "")
|
|
957
|
-
continue;
|
|
958
1252
|
const scope = SCOPE_REGISTRY.exec(key)?.[1];
|
|
959
1253
|
const tokenFor = AUTH_TOKEN.exec(key)?.[1];
|
|
1254
|
+
const raw = line.slice(at + 1).trim().replace(QUOTES, "$2");
|
|
1255
|
+
const value = tokenFor === undefined ? literal(raw) : expand2(raw, env);
|
|
1256
|
+
if (value === undefined || value === "")
|
|
1257
|
+
continue;
|
|
960
1258
|
if (scope !== undefined)
|
|
961
1259
|
config.scopes[scope] = value;
|
|
962
1260
|
else if (tokenFor !== undefined)
|
|
@@ -968,8 +1266,8 @@ function parseNpmrc(text, env) {
|
|
|
968
1266
|
}
|
|
969
1267
|
function bunEntry(entry, env) {
|
|
970
1268
|
const url = typeof entry === "string" ? entry : entry.url;
|
|
971
|
-
const token = typeof entry === "string" || entry.token === undefined ? undefined :
|
|
972
|
-
return { ...url === undefined ? {} : { url
|
|
1269
|
+
const token = typeof entry === "string" || entry.token === undefined ? undefined : expand2(entry.token, env);
|
|
1270
|
+
return { ...url === undefined || literal(url) === undefined ? {} : { url }, ...token === undefined ? {} : { token } };
|
|
973
1271
|
}
|
|
974
1272
|
function parseBunfig(text, env) {
|
|
975
1273
|
const config = { scopes: {}, tokens: {}, scopeTokens: {} };
|
|
@@ -991,26 +1289,35 @@ function parseBunfig(text, env) {
|
|
|
991
1289
|
return config;
|
|
992
1290
|
}
|
|
993
1291
|
function tokenFor(config, url) {
|
|
994
|
-
const address = url.replace(PROTOCOL, "")
|
|
995
|
-
const best = Object.keys(config.tokens).filter((key) => address.startsWith(key)).sort((a, b) => b.length - a.length)[0];
|
|
1292
|
+
const address = `${url.replace(PROTOCOL, "").replace(TRAILING_SLASHES2, "")}/`;
|
|
1293
|
+
const best = Object.keys(config.tokens).filter((key) => address.startsWith(`${key.replace(TRAILING_SLASHES2, "")}/`)).sort((a, b) => b.length - a.length)[0];
|
|
996
1294
|
return best === undefined ? undefined : config.tokens[best];
|
|
997
1295
|
}
|
|
1296
|
+
function mayCarryToken(url) {
|
|
1297
|
+
const { protocol, hostname } = new URL(url);
|
|
1298
|
+
return protocol === "https:" || LOCAL_HOSTS.has(hostname);
|
|
1299
|
+
}
|
|
998
1300
|
function registryFor(name, configs) {
|
|
999
1301
|
const scope = name.startsWith("@") ? name.slice(0, name.indexOf("/")) : undefined;
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1302
|
+
const scoped = scope === undefined ? undefined : configs.find((config) => config.scopes[scope] !== undefined);
|
|
1303
|
+
const url = (scope === undefined ? undefined : scoped?.scopes[scope]) ?? configs.find((config) => config.registry !== undefined)?.registry;
|
|
1304
|
+
if (url === undefined)
|
|
1305
|
+
return { url: PUBLIC_REGISTRY };
|
|
1306
|
+
const token = (scope === undefined ? undefined : scoped?.scopeTokens[scope]) ?? configs.map((other) => tokenFor(other, url)).find((found) => found !== undefined);
|
|
1307
|
+
return { url, token: mayCarryToken(url) ? token : undefined };
|
|
1308
|
+
}
|
|
1309
|
+
function readable(path, parse) {
|
|
1310
|
+
try {
|
|
1311
|
+
return parse();
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
return unreachable(`${path} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
1006
1314
|
}
|
|
1007
|
-
return { url: PUBLIC_REGISTRY };
|
|
1008
1315
|
}
|
|
1009
1316
|
async function readIfThere(path) {
|
|
1010
1317
|
const file = Bun.file(path);
|
|
1011
1318
|
return await file.exists() ? file.text() : undefined;
|
|
1012
1319
|
}
|
|
1013
|
-
async function loadConfigs(cwd =
|
|
1320
|
+
async function loadConfigs(cwd = process8.cwd(), env = process8.env, home = homedir()) {
|
|
1014
1321
|
const directories = [];
|
|
1015
1322
|
for (let dir = cwd;!directories.includes(dir); dir = dirname(dir))
|
|
1016
1323
|
directories.push(dir);
|
|
@@ -1021,7 +1328,7 @@ async function loadConfigs(cwd = process7.cwd(), env = process7.env, home = home
|
|
|
1021
1328
|
const bunfig = await readIfThere(join(dir, "bunfig.toml"));
|
|
1022
1329
|
const npmrc = await readIfThere(join(dir, ".npmrc"));
|
|
1023
1330
|
if (bunfig !== undefined)
|
|
1024
|
-
configs.push(parseBunfig(bunfig, env));
|
|
1331
|
+
configs.push(readable(join(dir, "bunfig.toml"), () => parseBunfig(bunfig, env)));
|
|
1025
1332
|
if (npmrc !== undefined)
|
|
1026
1333
|
configs.push(parseNpmrc(npmrc, env));
|
|
1027
1334
|
}
|
|
@@ -1039,7 +1346,7 @@ function parseSettings(text) {
|
|
|
1039
1346
|
settings.blockSeverity = raw.blockSeverity;
|
|
1040
1347
|
return settings;
|
|
1041
1348
|
}
|
|
1042
|
-
async function loadSettings(cwd =
|
|
1349
|
+
async function loadSettings(cwd = process8.cwd(), home = homedir()) {
|
|
1043
1350
|
const files = [];
|
|
1044
1351
|
for (let dir = cwd;!files.includes(join(dir, SETTINGS_FILE)); dir = dirname(dir))
|
|
1045
1352
|
files.push(join(dir, SETTINGS_FILE));
|
|
@@ -1049,13 +1356,13 @@ async function loadSettings(cwd = process7.cwd(), home = homedir()) {
|
|
|
1049
1356
|
const text = await readIfThere(file);
|
|
1050
1357
|
if (text === undefined)
|
|
1051
1358
|
continue;
|
|
1052
|
-
const found = parseSettings(text);
|
|
1359
|
+
const found = readable(file, () => parseSettings(text));
|
|
1053
1360
|
settings.licenses ??= found.licenses;
|
|
1054
1361
|
settings.blockSeverity ??= found.blockSeverity;
|
|
1055
1362
|
}
|
|
1056
1363
|
return settings;
|
|
1057
1364
|
}
|
|
1058
|
-
var ENV_REFERENCE, COMMENT, SCOPE_REGISTRY, AUTH_TOKEN, PROTOCOL, QUOTES, SEVERITIES, SETTINGS_FILE = ".pkgrc.json";
|
|
1365
|
+
var ENV_REFERENCE, COMMENT, SCOPE_REGISTRY, AUTH_TOKEN, PROTOCOL, TRAILING_SLASHES2, LOCAL_HOSTS, QUOTES, SEVERITIES, DEFAULT_BLOCK_SEVERITY = "high", SETTINGS_FILE = ".pkgrc.json";
|
|
1059
1366
|
var init_config2 = __esm(() => {
|
|
1060
1367
|
init_registry();
|
|
1061
1368
|
ENV_REFERENCE = /\$\{(\w+)\}|\$(\w+)/g;
|
|
@@ -1063,35 +1370,89 @@ var init_config2 = __esm(() => {
|
|
|
1063
1370
|
SCOPE_REGISTRY = /^(@[^:]+):registry$/;
|
|
1064
1371
|
AUTH_TOKEN = /^(\/\/.+?):_authToken$/;
|
|
1065
1372
|
PROTOCOL = /^\w+:/;
|
|
1373
|
+
TRAILING_SLASHES2 = /\/+$/;
|
|
1374
|
+
LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
1066
1375
|
QUOTES = /^(["'])(.*)\1$/;
|
|
1067
1376
|
SEVERITIES = ["low", "moderate", "high", "critical"];
|
|
1068
1377
|
});
|
|
1069
1378
|
|
|
1379
|
+
// src/semver.ts
|
|
1380
|
+
function isRange(text) {
|
|
1381
|
+
if (text.trim() === "")
|
|
1382
|
+
return true;
|
|
1383
|
+
return text.split(OR).every((alternative) => {
|
|
1384
|
+
const words = alternative.trim().split(WHITESPACE);
|
|
1385
|
+
return words.every((word, index) => word === "-" ? index > 0 && index < words.length - 1 : COMPARATOR.test(word));
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
function compare(version, word) {
|
|
1389
|
+
const bound = BOUND.exec(word);
|
|
1390
|
+
if (bound === null)
|
|
1391
|
+
return;
|
|
1392
|
+
const order = Bun.semver.order(version, bound[2] ?? "");
|
|
1393
|
+
const operator = bound[1] ?? "=";
|
|
1394
|
+
if (operator === "<")
|
|
1395
|
+
return order < 0;
|
|
1396
|
+
if (operator === "<=")
|
|
1397
|
+
return order <= 0;
|
|
1398
|
+
if (operator === ">")
|
|
1399
|
+
return order > 0;
|
|
1400
|
+
return operator === ">=" ? order >= 0 : order === 0;
|
|
1401
|
+
}
|
|
1402
|
+
function inRange(version, range) {
|
|
1403
|
+
if (Bun.semver.satisfies(version, range))
|
|
1404
|
+
return true;
|
|
1405
|
+
if (!PRERELEASE.test(version))
|
|
1406
|
+
return false;
|
|
1407
|
+
return range.split(OR).some((alternative) => alternative.trim().split(WHITESPACE).every((word) => compare(version, word) ?? Bun.semver.satisfies(version, word)));
|
|
1408
|
+
}
|
|
1409
|
+
var OR, WHITESPACE, PRERELEASE, PART, COMPARATOR, BOUND, EXACT_VERSION;
|
|
1410
|
+
var init_semver = __esm(() => {
|
|
1411
|
+
OR = /\s*\|\|\s*/;
|
|
1412
|
+
WHITESPACE = /\s+/;
|
|
1413
|
+
PRERELEASE = /-/;
|
|
1414
|
+
PART = String.raw`(?:\d+|[xX*])`;
|
|
1415
|
+
COMPARATOR = new RegExp(String.raw`^(?:[<>]=?|=|\^|~>?)?v?${PART}(?:\.${PART}){0,2}(?:-[\w.-]+)?(?:\+[\w.-]+)?$`);
|
|
1416
|
+
BOUND = /^(<=|>=|[<>=])?v?(\d+\.\d+\.\d+(?:-[\w.-]+)?)$/;
|
|
1417
|
+
EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/;
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1070
1420
|
// src/advisories.ts
|
|
1071
1421
|
function severityRank(severity) {
|
|
1072
1422
|
return SEVERITIES.indexOf(severity);
|
|
1073
1423
|
}
|
|
1074
1424
|
function fixedVersion(vulnerable, version) {
|
|
1075
|
-
const range = vulnerable.split("||").map((each) => each.trim()).find((each) =>
|
|
1425
|
+
const range = vulnerable.split("||").map((each) => each.trim()).find((each) => inRange(version, each));
|
|
1076
1426
|
return range === undefined ? undefined : UPPER_BOUND.exec(range)?.[1];
|
|
1077
1427
|
}
|
|
1078
1428
|
async function fetchAdvisories(versions, registry, fetcher = fetch) {
|
|
1079
|
-
const url = `${registry.url.replace(
|
|
1429
|
+
const url = `${registry.url.replace(TRAILING_SLASHES3, "")}${BULK_PATH}`;
|
|
1430
|
+
const host = new URL(registry.url).host;
|
|
1080
1431
|
let response;
|
|
1081
1432
|
try {
|
|
1082
1433
|
response = await fetcher(url, {
|
|
1083
1434
|
method: "POST",
|
|
1084
1435
|
headers: { accept: "application/json", "content-type": "application/json", ...registry.token === undefined ? {} : { authorization: `Bearer ${registry.token}` } },
|
|
1085
|
-
body: JSON.stringify(versions)
|
|
1436
|
+
body: JSON.stringify(versions),
|
|
1437
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT2)
|
|
1086
1438
|
});
|
|
1087
|
-
} catch {
|
|
1088
|
-
return {};
|
|
1439
|
+
} catch (error) {
|
|
1440
|
+
return { found: {}, unavailable: `cannot reach ${host} for advisories: ${error instanceof Error ? error.message : String(error)}` };
|
|
1089
1441
|
}
|
|
1442
|
+
if (NO_ENDPOINT.has(response.status))
|
|
1443
|
+
return { found: {}, isUnsupported: true, unavailable: `${host} has no advisory endpoint` };
|
|
1090
1444
|
if (!response.ok)
|
|
1091
|
-
return {};
|
|
1092
|
-
|
|
1445
|
+
return { found: {}, unavailable: `${host} answered ${response.status} for advisories` };
|
|
1446
|
+
let body;
|
|
1447
|
+
try {
|
|
1448
|
+
body = await response.json();
|
|
1449
|
+
} catch {
|
|
1450
|
+
return { found: {}, unavailable: `${host} answered advisories with something that is not JSON` };
|
|
1451
|
+
}
|
|
1093
1452
|
const found = {};
|
|
1094
1453
|
for (const [name, raws] of Object.entries(body)) {
|
|
1454
|
+
if (!Array.isArray(raws))
|
|
1455
|
+
continue;
|
|
1095
1456
|
found[name] = raws.filter((raw) => SEVERITIES.includes(raw.severity)).map((raw) => ({
|
|
1096
1457
|
id: raw.id,
|
|
1097
1458
|
ghsa: GHSA.exec(raw.url)?.[0],
|
|
@@ -1101,20 +1462,22 @@ async function fetchAdvisories(versions, registry, fetcher = fetch) {
|
|
|
1101
1462
|
vulnerable: raw.vulnerable_versions
|
|
1102
1463
|
}));
|
|
1103
1464
|
}
|
|
1104
|
-
return found;
|
|
1465
|
+
return { found };
|
|
1105
1466
|
}
|
|
1106
1467
|
function advisoriesFor(advisories, version) {
|
|
1107
|
-
return (advisories ?? []).filter((each) =>
|
|
1468
|
+
return (advisories ?? []).filter((each) => inRange(version, each.vulnerable)).map((each) => ({ ...each, fixed: fixedVersion(each.vulnerable, version) }));
|
|
1108
1469
|
}
|
|
1109
1470
|
function filterAdvisories(advisories, level, ignore = []) {
|
|
1110
1471
|
return advisories.filter((each) => severityRank(each.severity) >= severityRank(level) && !ignore.includes(each.ghsa ?? "") && !ignore.includes(String(each.id)));
|
|
1111
1472
|
}
|
|
1112
|
-
var BULK_PATH = "/-/npm/v1/security/advisories/bulk",
|
|
1473
|
+
var BULK_PATH = "/-/npm/v1/security/advisories/bulk", TRAILING_SLASHES3, REQUEST_TIMEOUT2 = 1e4, GHSA, UPPER_BOUND, NO_ENDPOINT;
|
|
1113
1474
|
var init_advisories = __esm(() => {
|
|
1114
1475
|
init_config2();
|
|
1115
|
-
|
|
1476
|
+
init_semver();
|
|
1477
|
+
TRAILING_SLASHES3 = /\/+$/;
|
|
1116
1478
|
GHSA = /GHSA-[\w-]+$/;
|
|
1117
1479
|
UPPER_BOUND = /(?:^|\s)<([\w.+-]+)/;
|
|
1480
|
+
NO_ENDPOINT = new Set([404, 405, 501]);
|
|
1118
1481
|
});
|
|
1119
1482
|
|
|
1120
1483
|
// src/licenses.ts
|
|
@@ -1641,20 +2004,23 @@ var init_popular = __esm(() => {
|
|
|
1641
2004
|
function baseName(name) {
|
|
1642
2005
|
return name.slice(name.indexOf("/") + 1);
|
|
1643
2006
|
}
|
|
1644
|
-
function
|
|
2007
|
+
function scopeOf2(name) {
|
|
1645
2008
|
return name.startsWith("@") ? name.slice(0, name.indexOf("/")) : "";
|
|
1646
2009
|
}
|
|
1647
2010
|
function squash(name) {
|
|
1648
2011
|
return name.toLowerCase().replace(SEPARATORS, "");
|
|
1649
2012
|
}
|
|
1650
2013
|
function resemblance(name, popular) {
|
|
1651
|
-
if (
|
|
2014
|
+
if (scopeOf2(name) !== scopeOf2(popular) && baseName(name) === baseName(popular) && baseName(name).length >= MIN_LENGTH)
|
|
1652
2015
|
return `${popular} under another scope`;
|
|
1653
2016
|
if (squash(name) === squash(popular))
|
|
1654
2017
|
return `${popular} spelled with other separators or case`;
|
|
2018
|
+
const shortest = Math.min(name.length, popular.length);
|
|
2019
|
+
const allowed = shortest >= MIN_LENGTH * 2 ? 2 : 1;
|
|
2020
|
+
if (shortest < MIN_LENGTH || Math.abs(name.length - popular.length) > allowed)
|
|
2021
|
+
return;
|
|
1655
2022
|
const edits = distance(name, popular);
|
|
1656
|
-
|
|
1657
|
-
return Math.min(name.length, popular.length) >= MIN_LENGTH && edits <= allowed ? `${popular}, ${edits} ${edits === 1 ? "edit" : "edits"} away` : undefined;
|
|
2023
|
+
return edits <= allowed ? `${popular}, ${edits} ${edits === 1 ? "edit" : "edits"} away` : undefined;
|
|
1658
2024
|
}
|
|
1659
2025
|
function nearMisses(name, popular = popular_default) {
|
|
1660
2026
|
if (popular.includes(name))
|
|
@@ -1690,6 +2056,8 @@ function resolve(packument, request) {
|
|
|
1690
2056
|
return tags[wanted];
|
|
1691
2057
|
if (versions.includes(wanted))
|
|
1692
2058
|
return wanted;
|
|
2059
|
+
if (!isRange(wanted))
|
|
2060
|
+
return;
|
|
1693
2061
|
return versions.filter((version) => Bun.semver.satisfies(version, wanted)).sort(Bun.semver.order).at(-1);
|
|
1694
2062
|
}
|
|
1695
2063
|
function licenseOf(license) {
|
|
@@ -1703,6 +2071,9 @@ function ageDays(published, now) {
|
|
|
1703
2071
|
const at = Date.parse(published ?? "");
|
|
1704
2072
|
return Number.isNaN(at) ? undefined : (now - at) / DAY;
|
|
1705
2073
|
}
|
|
2074
|
+
function daysAgo(age) {
|
|
2075
|
+
return age < 1 ? "less than a day" : `${Math.floor(age)} day${Math.floor(age) === 1 ? "" : "s"}`;
|
|
2076
|
+
}
|
|
1706
2077
|
function installScriptsOf(version) {
|
|
1707
2078
|
return Object.fromEntries(INSTALL_SCRIPTS.flatMap((name) => version?.scripts?.[name] === undefined ? [] : [[name, version.scripts[name]]]));
|
|
1708
2079
|
}
|
|
@@ -1713,7 +2084,7 @@ function safeRelease(packument, now, minAgeDays) {
|
|
|
1713
2084
|
}
|
|
1714
2085
|
function analyze(input) {
|
|
1715
2086
|
const { spec, packument, registry, now = Date.now(), minAgeDays = DEFAULT_MIN_AGE_DAYS } = input;
|
|
1716
|
-
const report = { name: spec.name, registry, exists: packument !== undefined, requested: spec.request, installScripts: {}, nearMisses: nearMisses(spec.name), advisories: input.advisories ?? [], warnings: [], blocked: false, downloads: input.downloads };
|
|
2087
|
+
const report = { name: spec.name, registry, exists: packument !== undefined, requested: spec.request, installScripts: {}, nearMisses: nearMisses(spec.name), advisories: input.advisories ?? [], fixes: [], warnings: [], blocked: false, downloads: input.downloads };
|
|
1717
2088
|
if (report.nearMisses.length > 0)
|
|
1718
2089
|
report.warnings.push(`name looks like ${report.nearMisses.join("; ")}, check it is the package meant`);
|
|
1719
2090
|
if (packument === undefined) {
|
|
@@ -1739,11 +2110,17 @@ function analyze(input) {
|
|
|
1739
2110
|
return report;
|
|
1740
2111
|
}
|
|
1741
2112
|
const age = ageDays(report.published, now);
|
|
2113
|
+
const kept = new Set(report.advisories.map((each) => each.id));
|
|
2114
|
+
report.fixes = report.safe?.version === version ? [] : (input.safeAdvisories ?? []).filter((each) => !kept.has(each.id));
|
|
1742
2115
|
if (age === undefined) {
|
|
1743
|
-
|
|
2116
|
+
const isBlocking = minAgeDays > 0 && registry === PUBLIC_HOST;
|
|
2117
|
+
report.blocked ||= isBlocking;
|
|
2118
|
+
report.warnings.push(`${registry} gives no publish date for ${version}, its age cannot be checked${isBlocking ? " and it is refused" : ""}`);
|
|
2119
|
+
} else if (age < minAgeDays && report.fixes.length > 0) {
|
|
2120
|
+
report.warnings.push(`${version} was published ${daysAgo(age)} ago, under ${minAgeDays} days, but it fixes ${report.fixes.map((each) => `${each.ghsa ?? each.id} (${each.severity})`).join(", ")} that ${report.safe?.version} has, so its age does not hold it back`);
|
|
1744
2121
|
} else if (age < minAgeDays) {
|
|
1745
2122
|
report.blocked = true;
|
|
1746
|
-
report.warnings.push(`${version} was published ${
|
|
2123
|
+
report.warnings.push(`${version} was published ${daysAgo(age)} ago, under ${minAgeDays} days${report.safe === undefined ? ", and no older stable version exists" : `, use ${spec.name}@${report.safe.version}`}`);
|
|
1747
2124
|
}
|
|
1748
2125
|
const scripts = Object.keys(report.installScripts);
|
|
1749
2126
|
if (scripts.length > 0)
|
|
@@ -1769,11 +2146,14 @@ function analyze(input) {
|
|
|
1769
2146
|
}
|
|
1770
2147
|
return report;
|
|
1771
2148
|
}
|
|
1772
|
-
var DEFAULT_MIN_AGE_DAYS = 14, DAY = 86400000, INSTALL_SCRIPTS, GIT_PREFIX, VERSION_SEPARATOR;
|
|
2149
|
+
var DEFAULT_MIN_AGE_DAYS = 14, DAY = 86400000, PUBLIC_HOST, INSTALL_SCRIPTS, GIT_PREFIX, VERSION_SEPARATOR;
|
|
1773
2150
|
var init_analyze = __esm(() => {
|
|
1774
2151
|
init_advisories();
|
|
1775
2152
|
init_licenses();
|
|
1776
2153
|
init_nearmiss();
|
|
2154
|
+
init_registry();
|
|
2155
|
+
init_semver();
|
|
2156
|
+
PUBLIC_HOST = new URL(PUBLIC_REGISTRY).host;
|
|
1777
2157
|
INSTALL_SCRIPTS = ["preinstall", "install", "postinstall"];
|
|
1778
2158
|
GIT_PREFIX = /^git\+/;
|
|
1779
2159
|
VERSION_SEPARATOR = /(?<=.)@/;
|
|
@@ -1782,7 +2162,8 @@ var init_analyze = __esm(() => {
|
|
|
1782
2162
|
// src/commands/check.ts
|
|
1783
2163
|
var exports_check = {};
|
|
1784
2164
|
__export(exports_check, {
|
|
1785
|
-
check: () => check
|
|
2165
|
+
check: () => check,
|
|
2166
|
+
checkPackage: () => checkPackage
|
|
1786
2167
|
});
|
|
1787
2168
|
async function checkPackage(text, context) {
|
|
1788
2169
|
const spec = parseSpec(text);
|
|
@@ -1791,11 +2172,17 @@ async function checkPackage(text, context) {
|
|
|
1791
2172
|
const downloads = packument !== undefined && isPublic(registry) ? await fetchDownloads(spec.name, context.fetcher) : undefined;
|
|
1792
2173
|
const input = { spec, packument, downloads, registry: new URL(registry.url).host, now: context.now, minAgeDays: context.minAgeDays, licenses: context.settings?.licenses };
|
|
1793
2174
|
const report = analyze(input);
|
|
1794
|
-
const
|
|
1795
|
-
if (
|
|
2175
|
+
const blockSeverity = context.settings?.blockSeverity ?? DEFAULT_BLOCK_SEVERITY;
|
|
2176
|
+
if (report.version === undefined)
|
|
1796
2177
|
return report;
|
|
1797
|
-
const
|
|
1798
|
-
|
|
2178
|
+
const safe = report.safe?.version;
|
|
2179
|
+
const { found, unavailable, isUnsupported } = await fetchAdvisories({ [spec.name]: safe === undefined || safe === report.version ? [report.version] : [report.version, safe] }, registry, context.fetcher);
|
|
2180
|
+
if (unavailable !== undefined && isUnsupported !== true)
|
|
2181
|
+
return unreachable(unavailable);
|
|
2182
|
+
const checked = analyze({ ...input, blockSeverity, advisories: advisoriesFor(found[spec.name], report.version), safeAdvisories: safe === undefined ? [] : filterAdvisories(advisoriesFor(found[spec.name], safe), blockSeverity) });
|
|
2183
|
+
if (unavailable !== undefined)
|
|
2184
|
+
checked.warnings.push(`${unavailable}, so its known advisories are not checked`);
|
|
2185
|
+
return checked;
|
|
1799
2186
|
}
|
|
1800
2187
|
function when(release, now) {
|
|
1801
2188
|
return release?.published === undefined ? "" : `, published ${formatAge(release.published, now)} (${release.published.slice(0, 10)})`;
|
|
@@ -1842,9 +2229,9 @@ var usage = `pkg [check] <name>[@version]...
|
|
|
1842
2229
|
|
|
1843
2230
|
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.
|
|
1844
2231
|
|
|
1845
|
-
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) refuses a version with an advisory at that level or above.
|
|
2232
|
+
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.
|
|
1846
2233
|
|
|
1847
|
-
Exits 0 when every package passes, 1 when one is missing, the version asked for is under the minimum age, its licence is outside the allowlist or an advisory reaches the threshold, 2 on a usage error.`, options;
|
|
2234
|
+
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;
|
|
1848
2235
|
var init_check = __esm(() => {
|
|
1849
2236
|
init_src();
|
|
1850
2237
|
init_advisories();
|
|
@@ -1858,6 +2245,15 @@ var init_check = __esm(() => {
|
|
|
1858
2245
|
});
|
|
1859
2246
|
|
|
1860
2247
|
// src/lockfile.ts
|
|
2248
|
+
function split(resolution) {
|
|
2249
|
+
const at = resolution.search(VERSION_SEPARATOR2);
|
|
2250
|
+
if (at === -1)
|
|
2251
|
+
return;
|
|
2252
|
+
const target = resolution.slice(at + 1);
|
|
2253
|
+
if (target.startsWith(NPM_ALIAS))
|
|
2254
|
+
return split(target.slice(NPM_ALIAS.length));
|
|
2255
|
+
return NOT_REGISTRY.test(target) ? undefined : { name: resolution.slice(0, at), version: target };
|
|
2256
|
+
}
|
|
1861
2257
|
function parseBunLock(text) {
|
|
1862
2258
|
const lock = Bun.JSONC.parse(text);
|
|
1863
2259
|
const seen = new Map;
|
|
@@ -1865,17 +2261,15 @@ function parseBunLock(text) {
|
|
|
1865
2261
|
const resolution = entry[0];
|
|
1866
2262
|
if (typeof resolution !== "string")
|
|
1867
2263
|
continue;
|
|
1868
|
-
const
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
if (at !== -1 && !NOT_REGISTRY.test(version))
|
|
1872
|
-
seen.set(`${name}@${version}`, { name, version });
|
|
2264
|
+
const locked = split(resolution);
|
|
2265
|
+
if (locked !== undefined)
|
|
2266
|
+
seen.set(`${locked.name}@${locked.version}`, locked);
|
|
1873
2267
|
}
|
|
1874
2268
|
return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name) || Bun.semver.order(a.version, b.version));
|
|
1875
2269
|
}
|
|
1876
|
-
var NOT_REGISTRY, VERSION_SEPARATOR2;
|
|
2270
|
+
var NOT_REGISTRY, VERSION_SEPARATOR2, NPM_ALIAS = "npm:";
|
|
1877
2271
|
var init_lockfile = __esm(() => {
|
|
1878
|
-
NOT_REGISTRY = /^(?:workspace|link|file|github|git|http|https
|
|
2272
|
+
NOT_REGISTRY = /^(?:workspace|link|file|github|git|http|https):|^[\w.-]+\/[\w.-]+$/;
|
|
1879
2273
|
VERSION_SEPARATOR2 = /(?<=.)@/;
|
|
1880
2274
|
});
|
|
1881
2275
|
|
|
@@ -1885,7 +2279,7 @@ __export(exports_audit, {
|
|
|
1885
2279
|
audit: () => audit
|
|
1886
2280
|
});
|
|
1887
2281
|
import { join as join2, resolve as resolve2 } from "path";
|
|
1888
|
-
import
|
|
2282
|
+
import process9 from "process";
|
|
1889
2283
|
function hasFinding(audited) {
|
|
1890
2284
|
return audited.blocked || audited.warnings.length > 0 || audited.advisories.length > 0;
|
|
1891
2285
|
}
|
|
@@ -1897,26 +2291,36 @@ async function auditPackages(packages, context) {
|
|
|
1897
2291
|
packuments.set(key, fetchPackument(name, registry, context.fetcher));
|
|
1898
2292
|
return packuments.get(key);
|
|
1899
2293
|
};
|
|
2294
|
+
const looked = [];
|
|
2295
|
+
for (let start = 0;start < packages.length; start += BATCH) {
|
|
2296
|
+
looked.push(...await Promise.all(packages.slice(start, start + BATCH).map(async ({ name, version }) => {
|
|
2297
|
+
const registry = registryFor(name, context.configs);
|
|
2298
|
+
const input = { spec: { name, request: version }, packument: await lookup(name, registry), registry: new URL(registry.url).host, now: context.now, minAgeDays: context.minAgeDays, licenses: context.settings.licenses };
|
|
2299
|
+
return { name, version, registry, input, safe: analyze(input).safe?.version };
|
|
2300
|
+
})));
|
|
2301
|
+
}
|
|
1900
2302
|
const registries = new Map;
|
|
1901
|
-
for (const { name, version } of
|
|
1902
|
-
const registry = registryFor(name, context.configs);
|
|
2303
|
+
for (const { name, version, registry, safe } of looked) {
|
|
1903
2304
|
const group = registries.get(registry.url) ?? { registry, versions: {} };
|
|
1904
|
-
group.versions[name] = [...group.versions[name] ?? [], version];
|
|
2305
|
+
group.versions[name] = [...group.versions[name] ?? [], version, ...safe === undefined || safe === version ? [] : [safe]];
|
|
1905
2306
|
registries.set(registry.url, group);
|
|
1906
2307
|
}
|
|
1907
2308
|
const advisories = {};
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
const found = filterAdvisories(advisoriesFor(advisories[name], version), context.level, context.ignore);
|
|
1915
|
-
const report = analyze({ spec: { name, request: version }, packument: await lookup(name, registry), registry: new URL(registry.url).host, now: context.now, minAgeDays: context.minAgeDays, licenses: context.settings.licenses });
|
|
1916
|
-
return { name, version, license: report.license, blocked: report.blocked || found.length > 0, warnings: report.warnings, advisories: found };
|
|
1917
|
-
})));
|
|
2309
|
+
const unavailable = [];
|
|
2310
|
+
for (const { registry, versions } of registries.values()) {
|
|
2311
|
+
const fetched = await fetchAdvisories(versions, registry, context.fetcher);
|
|
2312
|
+
Object.assign(advisories, fetched.found);
|
|
2313
|
+
if (fetched.unavailable !== undefined)
|
|
2314
|
+
unavailable.push(fetched.unavailable);
|
|
1918
2315
|
}
|
|
1919
|
-
|
|
2316
|
+
const blockSeverity = context.settings.blockSeverity ?? DEFAULT_BLOCK_SEVERITY;
|
|
2317
|
+
const audited = looked.map(({ name, version, input, safe }) => {
|
|
2318
|
+
const found = filterAdvisories(advisoriesFor(advisories[name], version), context.level, context.ignore);
|
|
2319
|
+
const safeAdvisories = safe === undefined ? [] : filterAdvisories(advisoriesFor(advisories[name], safe), blockSeverity, context.ignore);
|
|
2320
|
+
const report = analyze({ ...input, advisories: advisoriesFor(advisories[name], version), safeAdvisories });
|
|
2321
|
+
return { name, version, license: report.license, blocked: report.blocked || found.length > 0, warnings: report.warnings, advisories: found };
|
|
2322
|
+
});
|
|
2323
|
+
return { audited, unavailable };
|
|
1920
2324
|
}
|
|
1921
2325
|
function renderAudit(findings, total) {
|
|
1922
2326
|
const blocks = findings.map(({ name, version, warnings, advisories }) => [
|
|
@@ -1934,7 +2338,7 @@ async function audit(args) {
|
|
|
1934
2338
|
const level = values["audit-level"] ?? "low";
|
|
1935
2339
|
if (!isSeverity(level))
|
|
1936
2340
|
fail(`--audit-level takes ${SEVERITIES.join(", ")}, not ${level}`);
|
|
1937
|
-
const dir = resolve2(asked ??
|
|
2341
|
+
const dir = resolve2(asked ?? process9.cwd());
|
|
1938
2342
|
const lock = Bun.file(join2(dir, "bun.lock"));
|
|
1939
2343
|
if (!await lock.exists())
|
|
1940
2344
|
fail(`no bun.lock in ${dir}`);
|
|
@@ -1946,19 +2350,24 @@ async function audit(args) {
|
|
|
1946
2350
|
level,
|
|
1947
2351
|
ignore: values.ignore ?? []
|
|
1948
2352
|
};
|
|
1949
|
-
const
|
|
2353
|
+
const { audited, unavailable } = await auditPackages(packages, context);
|
|
2354
|
+
const findings = audited.filter(hasFinding);
|
|
1950
2355
|
if (values.json === true)
|
|
1951
2356
|
printJson(findings);
|
|
1952
2357
|
else
|
|
1953
2358
|
console.log(renderAudit(findings, packages.length));
|
|
1954
|
-
|
|
2359
|
+
for (const reason of unavailable)
|
|
2360
|
+
warn(`${reason}, so its advisories are not checked`);
|
|
2361
|
+
if (findings.some((each) => each.blocked))
|
|
2362
|
+
return 1;
|
|
2363
|
+
return unavailable.length > 0 ? 2 : 0;
|
|
1955
2364
|
}
|
|
1956
2365
|
var usage2 = `pkg audit [dir]
|
|
1957
2366
|
|
|
1958
2367
|
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.
|
|
1959
2368
|
Findings are grouped per package, each registry package looked up once per run.
|
|
1960
2369
|
|
|
1961
|
-
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 on a usage error or without a bun.lock.`, options2, BATCH = 8;
|
|
2370
|
+
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;
|
|
1962
2371
|
var init_audit = __esm(() => {
|
|
1963
2372
|
init_src();
|
|
1964
2373
|
init_advisories();
|
|
@@ -1974,125 +2383,42 @@ var init_audit = __esm(() => {
|
|
|
1974
2383
|
};
|
|
1975
2384
|
});
|
|
1976
2385
|
|
|
1977
|
-
// ../kit/src/progress.ts
|
|
1978
|
-
import process9 from "process";
|
|
1979
|
-
function liveRegion(render, stream = process9.stderr, env = process9.env) {
|
|
1980
|
-
if (!takesControlCodes(stream, env))
|
|
1981
|
-
return { redraw: () => {
|
|
1982
|
-
return;
|
|
1983
|
-
}, print: (text) => void stream.write(`${text}
|
|
1984
|
-
`), stop: () => {
|
|
1985
|
-
return;
|
|
1986
|
-
} };
|
|
1987
|
-
const write = stream.write.bind(stream);
|
|
1988
|
-
let frame = 0;
|
|
1989
|
-
let drawn = 0;
|
|
1990
|
-
const clear = () => {
|
|
1991
|
-
if (drawn > 0)
|
|
1992
|
-
write(`${CLEAR_LINE}${LINE_UP.repeat(drawn - 1)}`);
|
|
1993
|
-
drawn = 0;
|
|
1994
|
-
};
|
|
1995
|
-
const draw = () => {
|
|
1996
|
-
const width = (stream.columns === undefined || stream.columns === 0 ? 80 : stream.columns) - 1;
|
|
1997
|
-
const height = stream.rows === undefined || stream.rows === 0 ? 24 : stream.rows;
|
|
1998
|
-
const lines = render(FRAMES[frame++ % FRAMES.length]).slice(0, Math.max(1, height - 1)).map((line) => cutToWidth(line, width));
|
|
1999
|
-
if (lines.length === 0) {
|
|
2000
|
-
clear();
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
write(`${drawn > 0 ? `${CLEAR_LINE}${LINE_UP.repeat(drawn - 1)}` : CLEAR_LINE}${lines.join(`
|
|
2004
|
-
`)}`);
|
|
2005
|
-
drawn = lines.length;
|
|
2006
|
-
};
|
|
2007
|
-
const timer = setInterval(draw, FRAME_MS);
|
|
2008
|
-
const restore = interceptOutput(clear);
|
|
2009
|
-
const onExit = () => clear();
|
|
2010
|
-
const signals = holdSignals(stop);
|
|
2011
|
-
function stop() {
|
|
2012
|
-
clearInterval(timer);
|
|
2013
|
-
clear();
|
|
2014
|
-
restore();
|
|
2015
|
-
process9.off("exit", onExit);
|
|
2016
|
-
signals.release();
|
|
2017
|
-
}
|
|
2018
|
-
timer.unref();
|
|
2019
|
-
process9.on("exit", onExit);
|
|
2020
|
-
return {
|
|
2021
|
-
redraw: draw,
|
|
2022
|
-
print: (text) => {
|
|
2023
|
-
clear();
|
|
2024
|
-
write(`${text}
|
|
2025
|
-
`);
|
|
2026
|
-
draw();
|
|
2027
|
-
},
|
|
2028
|
-
stop
|
|
2029
|
-
};
|
|
2030
|
-
}
|
|
2031
|
-
function cutToWidth(line, width) {
|
|
2032
|
-
if (Bun.stringWidth(line) <= width)
|
|
2033
|
-
return line;
|
|
2034
|
-
let room = width;
|
|
2035
|
-
let kept = "";
|
|
2036
|
-
let index = 0;
|
|
2037
|
-
let linked = false;
|
|
2038
|
-
while (index < line.length) {
|
|
2039
|
-
ESCAPE_CODE.lastIndex = index;
|
|
2040
|
-
const escape = ESCAPE_CODE.exec(line);
|
|
2041
|
-
if (escape !== null) {
|
|
2042
|
-
kept += escape[0];
|
|
2043
|
-
linked ||= escape[0].startsWith(`${ESC}]`);
|
|
2044
|
-
index = ESCAPE_CODE.lastIndex;
|
|
2045
|
-
continue;
|
|
2046
|
-
}
|
|
2047
|
-
const char = String.fromCodePoint(line.codePointAt(index));
|
|
2048
|
-
const columns = Bun.stringWidth(char);
|
|
2049
|
-
if (columns > room)
|
|
2050
|
-
break;
|
|
2051
|
-
kept += char;
|
|
2052
|
-
room -= columns;
|
|
2053
|
-
index += char.length;
|
|
2054
|
-
}
|
|
2055
|
-
return `${kept}${RESET_STYLE}${linked ? CLOSE_LINK : ""}`;
|
|
2056
|
-
}
|
|
2057
|
-
function interceptOutput(clear) {
|
|
2058
|
-
const targets = [...CONSOLE_METHODS.map((name) => [console, name]), [process9.stdout, "write"], [process9.stderr, "write"]];
|
|
2059
|
-
const wrapped = targets.map(([target, name]) => {
|
|
2060
|
-
const original = Reflect.get(target, name);
|
|
2061
|
-
const wrapper = (...args) => {
|
|
2062
|
-
clear();
|
|
2063
|
-
return original.apply(target, args);
|
|
2064
|
-
};
|
|
2065
|
-
Reflect.set(target, name, wrapper);
|
|
2066
|
-
return { target, name, original, wrapper };
|
|
2067
|
-
});
|
|
2068
|
-
return () => {
|
|
2069
|
-
for (const { target, name, original, wrapper } of wrapped) {
|
|
2070
|
-
if (Reflect.get(target, name) === wrapper)
|
|
2071
|
-
Reflect.set(target, name, original);
|
|
2072
|
-
}
|
|
2073
|
-
};
|
|
2074
|
-
}
|
|
2075
|
-
var FRAMES, FRAME_MS = 80, CLEAR_LINE = "\r\x1B[2K", LINE_UP = "\x1B[1A\x1B[2K", ESC, ESCAPE_CODE, RESET_STYLE, CLOSE_LINK, CONSOLE_METHODS;
|
|
2076
|
-
var init_progress = __esm(() => {
|
|
2077
|
-
init_format();
|
|
2078
|
-
init_process();
|
|
2079
|
-
init_style();
|
|
2080
|
-
FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2081
|
-
ESC = String.fromCharCode(27);
|
|
2082
|
-
ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x7E]|\\][^\\u0007${ESC}]*(?:\\u0007|${ESC}\\\\))`, "y");
|
|
2083
|
-
RESET_STYLE = `${ESC}[0m`;
|
|
2084
|
-
CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
2085
|
-
CONSOLE_METHODS = ["log", "info", "warn", "error", "debug", "dir", "dirxml", "table", "trace", "group", "groupCollapsed", "count", "timeEnd", "timeLog", "assert"];
|
|
2086
|
-
});
|
|
2087
|
-
|
|
2088
2386
|
// ../kit/src/shell.ts
|
|
2089
|
-
|
|
2387
|
+
function wrapper(short = "", long = [], operands = 0) {
|
|
2388
|
+
return { short, long, operands };
|
|
2389
|
+
}
|
|
2390
|
+
var SEPARATORS2, PLAIN_BREAKERS, GLOB_CHARS, COMMENT_PREFIXES, DOUBLE_QUOTED_ESCAPES, KEYWORDS, SHELLS, COMMAND_STRING_PROGRAMS, SHELL_VALUE_OPTIONS, PRINTERS, FIND_EXEC, WRAPPERS;
|
|
2090
2391
|
var init_shell = __esm(() => {
|
|
2091
2392
|
SEPARATORS2 = new Set([`
|
|
2092
2393
|
`, ";", "|"]);
|
|
2093
2394
|
PLAIN_BREAKERS = new Set(["<", ">", "(", ")", "&", "|"]);
|
|
2395
|
+
GLOB_CHARS = new Set(["*", "?", "["]);
|
|
2396
|
+
COMMENT_PREFIXES = new Set([";", "&", "|", "(", ")"]);
|
|
2094
2397
|
DOUBLE_QUOTED_ESCAPES = new Set(["$", "`", '"', "\\"]);
|
|
2095
|
-
|
|
2398
|
+
KEYWORDS = new Set(["then", "do", "else", "if", "elif", "while", "until", "!", "{", "}"]);
|
|
2399
|
+
SHELLS = new Set(["sh", "bash", "zsh", "dash", "ash", "ksh"]);
|
|
2400
|
+
COMMAND_STRING_PROGRAMS = new Set([...SHELLS, "su", "runuser"]);
|
|
2401
|
+
SHELL_VALUE_OPTIONS = new Set(["-o", "-O", "--rcfile", "--init-file"]);
|
|
2402
|
+
PRINTERS = new Set(["echo", "printf"]);
|
|
2403
|
+
FIND_EXEC = new Set(["-exec", "-execdir", "-ok", "-okdir"]);
|
|
2404
|
+
WRAPPERS = new Map([
|
|
2405
|
+
["command", wrapper()],
|
|
2406
|
+
["builtin", wrapper()],
|
|
2407
|
+
["nohup", wrapper()],
|
|
2408
|
+
["setsid", wrapper()],
|
|
2409
|
+
["busybox", wrapper()],
|
|
2410
|
+
["exec", wrapper("a")],
|
|
2411
|
+
["time", wrapper("fo", ["--format", "--output"])],
|
|
2412
|
+
["env", wrapper("uCS", ["--unset", "--chdir", "--split-string", "--argv0"])],
|
|
2413
|
+
["sudo", wrapper("ughpCTrtUDR", ["--user", "--group", "--host", "--prompt", "--close-from", "--command-timeout", "--role", "--type", "--other-user", "--chdir", "--chroot"])],
|
|
2414
|
+
["doas", wrapper("uC")],
|
|
2415
|
+
["timeout", wrapper("sk", ["--signal", "--kill-after"], 1)],
|
|
2416
|
+
["nice", wrapper("n", ["--adjustment"])],
|
|
2417
|
+
["ionice", wrapper("cnpPu", ["--class", "--classdata", "--pid", "--pgid", "--uid"])],
|
|
2418
|
+
["stdbuf", wrapper("ioe", ["--input", "--output", "--error"])],
|
|
2419
|
+
["chroot", wrapper("", ["--userspec", "--groups"], 1)],
|
|
2420
|
+
["xargs", wrapper("IndsEaPL", ["--max-args", "--max-procs", "--max-chars", "--delimiter", "--arg-file", "--eof", "--max-lines", "--process-slot-var"])]
|
|
2421
|
+
]);
|
|
2096
2422
|
});
|
|
2097
2423
|
|
|
2098
2424
|
// src/pin.ts
|
|
@@ -2187,7 +2513,7 @@ var init_consumers = __esm(() => {
|
|
|
2187
2513
|
init_pin();
|
|
2188
2514
|
KINDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
|
|
2189
2515
|
SKIPPED = new Set(["node_modules", "dist", "build", "vendor", "target", "coverage"]);
|
|
2190
|
-
LOCAL_SPEC =
|
|
2516
|
+
LOCAL_SPEC = /[:/]/;
|
|
2191
2517
|
KIND_FLAGS = { dependencies: [], devDependencies: ["--dev"], optionalDependencies: ["--optional"], peerDependencies: ["--peer"] };
|
|
2192
2518
|
});
|
|
2193
2519
|
|
|
@@ -2200,23 +2526,43 @@ __export(exports_bump, {
|
|
|
2200
2526
|
import { existsSync as existsSync2 } from "fs";
|
|
2201
2527
|
import { homedir as homedir2 } from "os";
|
|
2202
2528
|
import { dirname as dirname3, join as join4, relative as relative2 } from "path";
|
|
2529
|
+
async function lookup(name, registry, fetcher) {
|
|
2530
|
+
try {
|
|
2531
|
+
return { packument: await fetchPackument(name, registry, fetcher) };
|
|
2532
|
+
} catch (error) {
|
|
2533
|
+
if (error instanceof Exit && error.code === 2)
|
|
2534
|
+
return { error: error.message };
|
|
2535
|
+
throw error;
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2203
2538
|
async function servedVersion(name, version, settings) {
|
|
2539
|
+
if (!isValidName(name))
|
|
2540
|
+
fail(`${name} is not a valid npm package name`);
|
|
2541
|
+
if (version !== undefined && !EXACT_VERSION.test(version))
|
|
2542
|
+
fail(`bump takes an exact version, not ${version}: leave it out for the latest`);
|
|
2204
2543
|
const registry = registryFor(name, settings.configs);
|
|
2205
2544
|
const host = new URL(registry.url).host;
|
|
2206
2545
|
const started = Date.now();
|
|
2207
2546
|
const region = liveRegion((spinner) => [`${spinner} waiting for ${host} to serve ${name}@${version} ${formatDuration(Date.now() - started)}`]);
|
|
2547
|
+
let lastError = "";
|
|
2208
2548
|
try {
|
|
2209
2549
|
for (;; ) {
|
|
2210
|
-
const
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2550
|
+
const looked = await lookup(name, registry, settings.fetcher);
|
|
2551
|
+
if ("error" in looked) {
|
|
2552
|
+
lastError = looked.error;
|
|
2553
|
+
} else {
|
|
2554
|
+
const latest = looked.packument?.["dist-tags"]?.latest;
|
|
2555
|
+
if (looked.packument === undefined)
|
|
2556
|
+
fail(`${host} has no ${name}`, 1);
|
|
2557
|
+
if (version === undefined && latest === undefined)
|
|
2558
|
+
fail(`${host} has no ${name}`, 1);
|
|
2559
|
+
if (version === undefined && latest !== undefined)
|
|
2560
|
+
return latest;
|
|
2561
|
+
if (version !== undefined && looked.packument.versions?.[version] !== undefined)
|
|
2562
|
+
return version;
|
|
2563
|
+
}
|
|
2218
2564
|
if (Date.now() - started >= settings.timeout)
|
|
2219
|
-
fail(`${host} does not serve ${name}@${version} after ${formatDuration(settings.timeout)}`);
|
|
2565
|
+
fail(`${host} does not serve ${name}@${version} after ${formatDuration(settings.timeout)}${lastError === "" ? "" : `, last error: ${lastError}`}`);
|
|
2220
2566
|
region.redraw();
|
|
2221
2567
|
await Bun.sleep(settings.poll ?? POLL);
|
|
2222
2568
|
}
|
|
@@ -2224,6 +2570,20 @@ async function servedVersion(name, version, settings) {
|
|
|
2224
2570
|
region.stop();
|
|
2225
2571
|
}
|
|
2226
2572
|
}
|
|
2573
|
+
async function vet(name, version, settings) {
|
|
2574
|
+
if (settings.isForced === true)
|
|
2575
|
+
return true;
|
|
2576
|
+
const report = await checkPackage(`${name}@${version}`, { configs: settings.configs, fetcher: settings.fetcher, minAgeDays: settings.minAgeDays ?? 0, settings: settings.pkgSettings });
|
|
2577
|
+
if (!report.blocked) {
|
|
2578
|
+
for (const warning of report.warnings)
|
|
2579
|
+
warn(`${name}@${version}`, warning);
|
|
2580
|
+
return true;
|
|
2581
|
+
}
|
|
2582
|
+
for (const warning of report.warnings)
|
|
2583
|
+
problem(`${name}@${version} is refused`, warning);
|
|
2584
|
+
problem(`${name}@${version} is refused`, "no consumer was touched, --force bumps it anyway");
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2227
2587
|
function lockfileOf(dir, root) {
|
|
2228
2588
|
for (let at = dir;; at = dirname3(at)) {
|
|
2229
2589
|
const found = ["bun.lock", "bun.lockb"].find((file) => existsSync2(join4(at, file)));
|
|
@@ -2233,6 +2593,11 @@ function lockfileOf(dir, root) {
|
|
|
2233
2593
|
return;
|
|
2234
2594
|
}
|
|
2235
2595
|
}
|
|
2596
|
+
async function hasUncommittedChanges(where, root, run) {
|
|
2597
|
+
const lockfile = lockfileOf(where, root);
|
|
2598
|
+
const status = await run(["git", "status", "--porcelain", "--", "package.json", ...lockfile === undefined ? [] : [lockfile]], where);
|
|
2599
|
+
return status.code === 0 && status.text.trim() !== "";
|
|
2600
|
+
}
|
|
2236
2601
|
async function checkAndCommit(name, version, where, root, run) {
|
|
2237
2602
|
const checked = await run(["check"], where);
|
|
2238
2603
|
if (checked.code !== 0)
|
|
@@ -2255,11 +2620,14 @@ async function bumpConsumers(name, version, settings) {
|
|
|
2255
2620
|
if (found.length === 0)
|
|
2256
2621
|
note(`nothing under ${settings.root} depends on ${name}`);
|
|
2257
2622
|
if (local > 0)
|
|
2258
|
-
note(`${local}
|
|
2623
|
+
note(`${local} non-registry dependenc${local === 1 ? "y" : "ies"} (workspace:, file:, git, URL, alias, catalog:)`, "left as they are");
|
|
2259
2624
|
for (const consumer of consumers) {
|
|
2260
2625
|
const argv = addCommand(consumer, name, version);
|
|
2261
2626
|
const where = join4(settings.root, consumer.dir);
|
|
2262
|
-
if (consumer.
|
|
2627
|
+
if (consumer.kind === "peerDependencies") {
|
|
2628
|
+
skipped.push(consumer.dir);
|
|
2629
|
+
note(consumer.dir, `peer range ${consumer.spec}, left as it is`);
|
|
2630
|
+
} else if (consumer.spec === version) {
|
|
2263
2631
|
skipped.push(consumer.dir);
|
|
2264
2632
|
note(consumer.dir, `already on ${version}`);
|
|
2265
2633
|
} else if (consumer.manager !== "bun") {
|
|
@@ -2267,6 +2635,10 @@ async function bumpConsumers(name, version, settings) {
|
|
|
2267
2635
|
warn(consumer.dir, `${consumer.spec}, its lockfile pins ${consumer.manager}: put ${version} in by hand`);
|
|
2268
2636
|
} else if (settings.isDryRun === true) {
|
|
2269
2637
|
note(consumer.dir, `would run ${[argv.join(" "), ...settings.isAll === true ? ["check", `gc 'chore(deps): bump ${name} to ${version}'`] : []].join(", then ")}`);
|
|
2638
|
+
} else if (settings.isAll === true && await hasUncommittedChanges(where, settings.root, run)) {
|
|
2639
|
+
isClean = false;
|
|
2640
|
+
failing.push(consumer.dir);
|
|
2641
|
+
problem(consumer.dir, "package.json or its lockfile has uncommitted changes, left alone so that gc does not commit them");
|
|
2270
2642
|
} else {
|
|
2271
2643
|
const ran = await run(argv, where);
|
|
2272
2644
|
if (ran.code !== 0) {
|
|
@@ -2300,30 +2672,40 @@ async function bump(args) {
|
|
|
2300
2672
|
configs: await loadConfigs(),
|
|
2301
2673
|
timeout: duration(values.timeout, "timeout", 10 * 60000),
|
|
2302
2674
|
isDryRun: values["dry-run"] === true,
|
|
2303
|
-
isAll: values["all-consumers"] === true
|
|
2675
|
+
isAll: values["all-consumers"] === true,
|
|
2676
|
+
isForced: values.force === true,
|
|
2677
|
+
pkgSettings: await loadSettings(),
|
|
2678
|
+
minAgeDays: wholeNumber(values["min-age"], "min-age", 0, 0)
|
|
2304
2679
|
};
|
|
2305
2680
|
const version = await servedVersion(name, asked, settings);
|
|
2681
|
+
if (!await vet(name, version, settings))
|
|
2682
|
+
return 1;
|
|
2306
2683
|
return await bumpConsumers(name, version, settings) ? 0 : 1;
|
|
2307
2684
|
}
|
|
2308
2685
|
var usage3 = `pkg bump <name> [version]
|
|
2309
2686
|
|
|
2310
|
-
Waits until the registry serves the version (the latest when none is given), 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.
|
|
2311
|
-
A
|
|
2312
|
-
|
|
2687
|
+
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.
|
|
2688
|
+
A version the vetting blocks is refused before any folder is touched, unless --force is given.
|
|
2689
|
+
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.
|
|
2690
|
+
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.
|
|
2313
2691
|
|
|
2314
|
-
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 an install, a check or a commit fails or the registry has no such package, 2 when the version never shows within the timeout or on a usage error.`, ROOT_OPTION, options3, POLL = 5000;
|
|
2692
|
+
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;
|
|
2315
2693
|
var init_bump = __esm(() => {
|
|
2316
2694
|
init_src();
|
|
2317
2695
|
init_progress();
|
|
2318
2696
|
init_config2();
|
|
2319
2697
|
init_consumers();
|
|
2320
2698
|
init_registry();
|
|
2699
|
+
init_semver();
|
|
2700
|
+
init_check();
|
|
2321
2701
|
ROOT_OPTION = { type: "string", description: "the folder holding the repositories (~/Workspace)" };
|
|
2322
2702
|
options3 = {
|
|
2323
2703
|
root: ROOT_OPTION,
|
|
2324
2704
|
timeout: { type: "string", short: "t", description: "how long to wait for the registry (10m): 30s, 5m, 1h" },
|
|
2325
2705
|
"all-consumers": { type: "boolean", description: "after each install run the folder's check and commit it through gc when it passes" },
|
|
2326
|
-
"dry-run": { type: "boolean", description: "print what would run, changing nothing" }
|
|
2706
|
+
"dry-run": { type: "boolean", description: "print what would run, changing nothing" },
|
|
2707
|
+
force: { type: "boolean", description: "bump even when the version is blocked by the vetting" },
|
|
2708
|
+
"min-age": { type: "string", description: "days a version must be old (0: a bump adopts a release on purpose, often one just made)" }
|
|
2327
2709
|
};
|
|
2328
2710
|
});
|
|
2329
2711
|
|
|
@@ -2367,7 +2749,7 @@ var init_consumers2 = __esm(() => {
|
|
|
2367
2749
|
init_src();
|
|
2368
2750
|
import process10 from "process";
|
|
2369
2751
|
// package.json
|
|
2370
|
-
var version = "0.
|
|
2752
|
+
var version = "0.6.0";
|
|
2371
2753
|
|
|
2372
2754
|
// src/cli.ts
|
|
2373
2755
|
process10.exitCode = await runCli({
|