@westbayberry/dg 1.0.53 → 1.0.56

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 (64) hide show
  1. package/README.md +5 -1
  2. package/dist/index.mjs +249 -114
  3. package/dist/packages/cli/src/alt-screen.js +36 -0
  4. package/dist/packages/cli/src/api.js +322 -0
  5. package/dist/packages/cli/src/auth.js +218 -0
  6. package/dist/packages/cli/src/bin.js +386 -0
  7. package/dist/packages/cli/src/config.js +228 -0
  8. package/dist/packages/cli/src/discover.js +126 -0
  9. package/dist/packages/cli/src/first-run.js +135 -0
  10. package/dist/packages/cli/src/hook.js +360 -0
  11. package/dist/packages/cli/src/lockfile.js +303 -0
  12. package/dist/packages/cli/src/npm-wrapper.js +218 -0
  13. package/dist/packages/cli/src/pip-wrapper.js +273 -0
  14. package/dist/packages/cli/src/sanitize.js +38 -0
  15. package/dist/packages/cli/src/scan-core.js +144 -0
  16. package/dist/packages/cli/src/setup-status.js +46 -0
  17. package/dist/packages/cli/src/static-output.js +625 -0
  18. package/dist/packages/cli/src/telemetry.js +141 -0
  19. package/dist/packages/cli/src/ui/App.js +137 -0
  20. package/dist/packages/cli/src/ui/InitApp.js +391 -0
  21. package/dist/packages/cli/src/ui/LoginApp.js +51 -0
  22. package/dist/packages/cli/src/ui/NpmWrapperApp.js +73 -0
  23. package/dist/packages/cli/src/ui/PipWrapperApp.js +72 -0
  24. package/dist/packages/cli/src/ui/components/ConfirmPrompt.js +24 -0
  25. package/dist/packages/cli/src/ui/components/DemoScanAnimation.js +26 -0
  26. package/dist/packages/cli/src/ui/components/DurationLine.js +7 -0
  27. package/dist/packages/cli/src/ui/components/ErrorView.js +30 -0
  28. package/dist/packages/cli/src/ui/components/FileSavePrompt.js +210 -0
  29. package/dist/packages/cli/src/ui/components/InteractiveResultsView.js +557 -0
  30. package/dist/packages/cli/src/ui/components/Mascot.js +33 -0
  31. package/dist/packages/cli/src/ui/components/ProgressBar.js +51 -0
  32. package/dist/packages/cli/src/ui/components/ProgressDots.js +35 -0
  33. package/dist/packages/cli/src/ui/components/ProjectSelector.js +60 -0
  34. package/dist/packages/cli/src/ui/components/ResultsView.js +105 -0
  35. package/dist/packages/cli/src/ui/components/ScanResultCard.js +54 -0
  36. package/dist/packages/cli/src/ui/components/ScoreHeader.js +142 -0
  37. package/dist/packages/cli/src/ui/components/SetupBanner.js +17 -0
  38. package/dist/packages/cli/src/ui/components/Spinner.js +11 -0
  39. package/dist/packages/cli/src/ui/hooks/useExpandAnimation.js +44 -0
  40. package/dist/packages/cli/src/ui/hooks/useInit.js +341 -0
  41. package/dist/packages/cli/src/ui/hooks/useLogin.js +121 -0
  42. package/dist/packages/cli/src/ui/hooks/useNpmWrapper.js +192 -0
  43. package/dist/packages/cli/src/ui/hooks/usePipWrapper.js +195 -0
  44. package/dist/packages/cli/src/ui/hooks/useScan.js +202 -0
  45. package/dist/packages/cli/src/ui/hooks/useTerminalSize.js +29 -0
  46. package/dist/packages/cli/src/update-check.js +152 -0
  47. package/dist/packages/cli/src/wizard-demo-data.js +63 -0
  48. package/dist/src/ecosystem.js +2 -0
  49. package/dist/src/lockfile/diff.js +38 -0
  50. package/dist/src/lockfile/parse_package_json.js +41 -0
  51. package/dist/src/lockfile/parse_package_lock.js +55 -0
  52. package/dist/src/lockfile/parse_pipfile_lock.js +69 -0
  53. package/dist/src/lockfile/parse_pnpm_lock.js +62 -0
  54. package/dist/src/lockfile/parse_poetry_lock.js +71 -0
  55. package/dist/src/lockfile/parse_requirements.js +83 -0
  56. package/dist/src/lockfile/parse_yarn_lock.js +66 -0
  57. package/dist/src/logger.js +21 -0
  58. package/dist/src/npm/h2pool.js +161 -0
  59. package/dist/src/npm/registry.js +299 -0
  60. package/dist/src/npm/tarball.js +274 -0
  61. package/dist/src/pypi/registry.js +299 -0
  62. package/dist/src/pypi/tarball.js +361 -0
  63. package/dist/src/types.js +2 -0
  64. package/package.json +6 -3
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProgressDots = void 0;
4
+ exports.phaseToStep = phaseToStep;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const ink_1 = require("ink");
7
+ const TOTAL_STEPS = 6;
8
+ // Maps wizard phases to step numbers for the progress indicator.
9
+ // Six groups: 1=Welcome, 2=Learn, 3=Your project, 4=Scan, 5=Next steps, 6=Done.
10
+ // Transient phases (detect, install_hook, etc.) share their parent's group.
11
+ function phaseToStep(phase) {
12
+ switch (phase) {
13
+ case "greet": return 1;
14
+ case "value_prop":
15
+ case "demo_scanning":
16
+ case "demo_result": return 2;
17
+ case "scenario":
18
+ case "detect":
19
+ case "confirm_hook":
20
+ case "install_hook":
21
+ case "verify_hook": return 3;
22
+ case "confirm_scan":
23
+ case "run_scan":
24
+ case "result_first_scan": return 4;
25
+ case "what_happens_next":
26
+ case "pitch_app": return 5;
27
+ case "done": return 6;
28
+ default: return 0;
29
+ }
30
+ }
31
+ const ProgressDots = ({ current }) => ((0, jsx_runtime_1.jsx)(ink_1.Text, { children: Array.from({ length: TOTAL_STEPS }).map((_, i) => {
32
+ const filled = i < current;
33
+ return ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: filled ? "green" : undefined, dimColor: !filled, children: filled ? "\u25CF " : "\u25CB " }, i));
34
+ }) }));
35
+ exports.ProgressDots = ProgressDots;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ProjectSelector = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const ink_1 = require("ink");
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ const sanitize_1 = require("../../sanitize");
12
+ const ProjectSelector = ({ projects, onConfirm, onCancel, userStatus }) => {
13
+ const [cursor, setCursor] = (0, react_1.useState)(0);
14
+ const [selected, setSelected] = (0, react_1.useState)(() => new Set(projects.map((_, i) => i)));
15
+ (0, ink_1.useInput)((input, key) => {
16
+ if (key.upArrow) {
17
+ setCursor((c) => Math.max(0, c - 1));
18
+ }
19
+ else if (key.downArrow) {
20
+ setCursor((c) => Math.min(projects.length - 1, c + 1));
21
+ }
22
+ else if (input === " ") {
23
+ setSelected((prev) => {
24
+ const next = new Set(prev);
25
+ if (next.has(cursor))
26
+ next.delete(cursor);
27
+ else
28
+ next.add(cursor);
29
+ return next;
30
+ });
31
+ }
32
+ else if (input === "a") {
33
+ setSelected((prev) => {
34
+ if (prev.size === projects.length)
35
+ return new Set();
36
+ return new Set(projects.map((_, i) => i));
37
+ });
38
+ }
39
+ else if (key.return) {
40
+ const picked = projects.filter((_, i) => selected.has(i));
41
+ if (picked.length > 0)
42
+ onConfirm(picked);
43
+ }
44
+ else if (input === "q") {
45
+ onCancel();
46
+ }
47
+ });
48
+ const ecosystemLabel = (eco) => eco === "npm" ? chalk_1.default.magenta("npm") : chalk_1.default.blue("pip");
49
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 1, children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.cyan("\u25C6"), " ", chalk_1.default.bold("Dependency Guardian"), " ", userStatus ? chalk_1.default.dim(userStatus) : ""] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, children: ["Found ", projects.length, " project", projects.length !== 1 ? "s" : ""] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }), projects.map((proj, i) => {
50
+ const isCursor = i === cursor;
51
+ const isSelected = selected.has(i);
52
+ const prefix = isCursor ? chalk_1.default.cyan("\u258C") : " ";
53
+ const check = isSelected ? chalk_1.default.green("\u25C9") : chalk_1.default.dim("\u25CB");
54
+ const line = `${prefix}${check} ${(0, sanitize_1.sanitize)(proj.relativePath).padEnd(40)} ${ecosystemLabel(proj.ecosystem).padEnd(5)} ${proj.packageCount} packages`;
55
+ return ((0, jsx_runtime_1.jsx)(ink_1.Text, { backgroundColor: isCursor ? "#1a1a2e" : undefined, children: line }, i));
56
+ }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: selected.size === 0
57
+ ? chalk_1.default.yellow("Select at least 1 project to scan")
58
+ : `${selected.size} of ${projects.length} selected` }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: "" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.bold.cyan("space"), " ", chalk_1.default.dim("toggle"), " ", chalk_1.default.bold.cyan("a"), " ", chalk_1.default.dim("all"), " ", chalk_1.default.bold.hex('#FFD700')("\u23CE"), " ", chalk_1.default.bold.hex('#FFD700')("scan"), " ", chalk_1.default.bold.cyan("q"), " ", chalk_1.default.dim("quit")] })] }));
59
+ };
60
+ exports.ProjectSelector = ProjectSelector;
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ResultsView = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const ink_1 = require("ink");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const ScoreHeader_1 = require("./ScoreHeader");
11
+ const DurationLine_1 = require("./DurationLine");
12
+ /**
13
+ * Group packages that share identical findings (e.g. @esbuild/* platform variants).
14
+ * Returns groups sorted by score descending.
15
+ */
16
+ function groupPackages(packages) {
17
+ const map = new Map();
18
+ for (const pkg of packages) {
19
+ const fingerprint = pkg.findings.length === 0
20
+ ? `__clean_${pkg.score}`
21
+ : pkg.findings
22
+ .map((f) => `${f.category ?? ""}:${f.severity}`)
23
+ .sort()
24
+ .join("|") + `|score:${pkg.score}`;
25
+ const group = map.get(fingerprint) ?? [];
26
+ group.push(pkg);
27
+ map.set(fingerprint, group);
28
+ }
29
+ return [...map.values()]
30
+ .map((pkgs) => ({ packages: pkgs, key: pkgs[0].name }))
31
+ .sort((a, b) => b.packages[0].score - a.packages[0].score);
32
+ }
33
+ const SEVERITY_LABELS = {
34
+ 5: "CRITICAL",
35
+ 4: "HIGH",
36
+ 3: "MEDIUM",
37
+ 2: "LOW",
38
+ 1: "INFO",
39
+ };
40
+ function severityColor(sev) {
41
+ if (sev >= 5)
42
+ return (s) => chalk_1.default.bold.red(s);
43
+ if (sev >= 4)
44
+ return chalk_1.default.magenta;
45
+ if (sev >= 3)
46
+ return chalk_1.default.yellow;
47
+ if (sev >= 2)
48
+ return chalk_1.default.dim;
49
+ return chalk_1.default.dim;
50
+ }
51
+ function actionBadge(score) {
52
+ if (score >= 70)
53
+ return { label: "BLOCK", color: chalk_1.default.red };
54
+ if (score >= 60)
55
+ return { label: "WARN", color: chalk_1.default.yellow };
56
+ return { label: "PASS", color: chalk_1.default.green };
57
+ }
58
+ function truncate(s, max) {
59
+ return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
60
+ }
61
+ function pad(s, len) {
62
+ return s + " ".repeat(Math.max(0, len - s.length));
63
+ }
64
+ const EVIDENCE_LIMIT = 3;
65
+ const ResultsView = ({ result, config: _config, durationMs, }) => {
66
+ const flagged = result.packages.filter((p) => p.score > 0);
67
+ const clean = result.packages.filter((p) => p.score === 0);
68
+ const total = result.packages.length;
69
+ const groups = groupPackages(flagged);
70
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingLeft: 2, children: [(0, jsx_runtime_1.jsx)(ink_1.Newline, {}), (0, jsx_runtime_1.jsx)(ScoreHeader_1.ScoreHeader, { score: result.score, action: result.action }), (0, jsx_runtime_1.jsx)(ink_1.Newline, {}), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`), flagged.length > 0 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" ", chalk_1.default.yellow(`${flagged.length} flagged`), " ", chalk_1.default.green(`${clean.length} clean`)] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" ", chalk_1.default.green("all clean")] }))] }), (0, jsx_runtime_1.jsx)(ink_1.Newline, {}), flagged.length > 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: "Flagged Packages" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: "\u2500".repeat(60) }), groups.map((group) => {
71
+ const rep = group.packages[0];
72
+ const { label, color } = actionBadge(rep.score);
73
+ const names = group.packages.length === 1
74
+ ? rep.name
75
+ : group.packages.length <= 3
76
+ ? group.packages.map((p) => p.name).join(", ")
77
+ : `${group.packages[0].name} + ${group.packages.length - 1} similar`;
78
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", color(pad(label, 7)), chalk_1.default.bold(pad(truncate(names, 50), 52)), chalk_1.default.dim(`score ${rep.score}`)] }, group.key));
79
+ }), (0, jsx_runtime_1.jsx)(ink_1.Newline, {})] })), clean.length > 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.green("\u2713"), " ", chalk_1.default.green.bold(String(clean.length)), " ", chalk_1.default.dim(`package${clean.length !== 1 ? "s" : ""} passed with score 0`)] })), (0, jsx_runtime_1.jsx)(ink_1.Newline, {}), groups
80
+ .filter((g) => g.packages[0].score > 0)
81
+ .map((group) => {
82
+ const rep = group.packages[0];
83
+ const names = group.packages.length === 1
84
+ ? `${rep.name}@${rep.version}`
85
+ : group.packages.length <= 3
86
+ ? group.packages.map((p) => `${p.name}@${p.version}`).join(", ")
87
+ : `${rep.name}@${rep.version} + ${group.packages.length - 1} identical packages`;
88
+ const visibleFindings = rep.findings
89
+ .filter((f) => f.severity > 1)
90
+ .sort((a, b) => b.severity - a.severity);
91
+ const safeVersion = result.safeVersions[rep.name];
92
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.dim("\u2500\u2500"), " ", chalk_1.default.bold(`${names} (score: ${rep.score})`)] }), group.packages.length > 3 && ((0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [" Affects: ", group.packages.map((p) => p.name).join(", ")] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), visibleFindings.map((finding, idx) => {
93
+ const sevLabel = SEVERITY_LABELS[finding.severity] ?? "INFO";
94
+ const colorFn = severityColor(finding.severity);
95
+ const evidence = finding.evidence ?? [];
96
+ const evidenceSlice = evidence.slice(0, EVIDENCE_LIMIT);
97
+ const overflow = evidence.length - EVIDENCE_LIMIT;
98
+ const label = finding.title
99
+ ? `${finding.category ?? ""} \u2014 ${finding.title}`
100
+ : finding.category ?? "";
101
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", colorFn(pad(sevLabel, 10)), label] }), evidenceSlice.map((ev, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", chalk_1.default.dim(truncate(ev, 76))] }, i))), overflow > 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", chalk_1.default.dim(`... and ${overflow} more`)] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " })] }, idx));
102
+ }), safeVersion && ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", chalk_1.default.green(`Safe version: ${rep.name}@${safeVersion}`)] })), (0, jsx_runtime_1.jsx)(ink_1.Newline, {})] }, group.key));
103
+ }), (0, jsx_runtime_1.jsx)(DurationLine_1.DurationLine, { ms: durationMs }), (0, jsx_runtime_1.jsx)(ink_1.Newline, {})] }));
104
+ };
105
+ exports.ResultsView = ResultsView;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScanResultCard = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ const wizard_demo_data_1 = require("../../wizard-demo-data");
7
+ const ScanResultCard = ({ outcome }) => {
8
+ let verdict;
9
+ let verdictColor;
10
+ let score;
11
+ let packages;
12
+ let extraNote = null;
13
+ if (outcome.kind === "demo") {
14
+ verdict = "BLOCKED";
15
+ verdictColor = "red";
16
+ score = outcome.score;
17
+ packages = [outcome.pkg];
18
+ }
19
+ else {
20
+ const o = outcome.outcome;
21
+ if (o.status === "ok" && o.result) {
22
+ const r = o.result.result;
23
+ verdict = r.action === "block" ? "BLOCKED" : r.action === "warn" ? "WARNING" : "PASSED";
24
+ verdictColor = r.action === "block" ? "red" : r.action === "warn" ? "yellow" : "green";
25
+ score = r.score;
26
+ packages = r.packages;
27
+ }
28
+ else if (o.status === "no_packages") {
29
+ verdict = "EMPTY";
30
+ verdictColor = "gray";
31
+ score = 0;
32
+ packages = [];
33
+ extraNote = "Nothing to scan yet. I'll catch new packages as you add them.";
34
+ }
35
+ else if (o.status === "trial_exhausted") {
36
+ verdict = "EMPTY";
37
+ verdictColor = "yellow";
38
+ score = 0;
39
+ packages = [];
40
+ extraNote = "Free trial used up. Run `dg login` to keep scanning.";
41
+ }
42
+ else {
43
+ verdict = "EMPTY";
44
+ verdictColor = "yellow";
45
+ score = 0;
46
+ packages = [];
47
+ extraNote = `Scan didn't finish: ${o.message ?? "unknown error"}`;
48
+ }
49
+ }
50
+ return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: verdict !== "EMPTY" && packages.length > 0 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [packages.length === 1 ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: packages[0].name }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: ["@", packages[0].version] })] })) : ((0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [packages.length, " packages"] })), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: verdictColor, bold: true, children: verdict }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " \u00B7 " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, children: [score, "/100"] })] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), packages.length === 1
51
+ ? packages[0].findings.slice(0, 3).map((f, i) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [" • ", (0, wizard_demo_data_1.friendlyCategory)(f.category), f.title ? ` — ${f.title}` : ""] }, `f-${i}`)))
52
+ : packages.slice(0, 3).map((pkg) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [" • ", (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, children: pkg.name }), " ", pkg.score, "/100"] }, pkg.name))), packages.length > 3 ? ((0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " + " + (packages.length - 3) + " more" })) : null] })) : verdict !== "EMPTY" ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: verdictColor, bold: true, children: verdict }), (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " \u00B7 " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, children: [score, "/100"] })] })) : ((0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: extraNote })) }));
53
+ };
54
+ exports.ScanResultCard = ScanResultCard;
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ScoreHeader = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const ink_1 = require("ink");
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ function scoreColor(score, action) {
11
+ const colorFn = action === "block" ? chalk_1.default.red.bold :
12
+ action === "warn" ? chalk_1.default.yellow.bold :
13
+ chalk_1.default.green.bold;
14
+ return colorFn(String(score));
15
+ }
16
+ const SEV_COLORS = {
17
+ 5: (s) => chalk_1.default.red(s),
18
+ 4: (s) => chalk_1.default.red(s),
19
+ 3: (s) => chalk_1.default.yellow(s),
20
+ 2: (s) => chalk_1.default.cyan(s),
21
+ };
22
+ const SEV_LABELS = {
23
+ 5: "CRIT",
24
+ 4: "HIGH",
25
+ 3: "MED",
26
+ 2: "LOW",
27
+ };
28
+ function severityBar(counts) {
29
+ const levels = [5, 4, 3, 2];
30
+ const total = levels.reduce((sum, lv) => sum + (counts[lv] ?? 0), 0);
31
+ if (total === 0)
32
+ return "";
33
+ const BAR_WIDTH = 30;
34
+ const parts = [];
35
+ for (const lv of levels) {
36
+ const count = counts[lv] ?? 0;
37
+ if (count === 0)
38
+ continue;
39
+ const width = Math.max(2, Math.round((count / total) * BAR_WIDTH));
40
+ const bar = "\u2501".repeat(width);
41
+ const colorFn = SEV_COLORS[lv] ?? chalk_1.default.dim;
42
+ parts.push(`${colorFn(bar)} ${count} ${SEV_LABELS[lv] ?? ""}`);
43
+ }
44
+ return parts.join(" ");
45
+ }
46
+ // Braille pixel-art logo: hub-and-spoke dependency graph
47
+ // Pixel helpers for drawing on a grid
48
+ function plot(grid, x, y, type) {
49
+ if (y >= 0 && y < grid.length && x >= 0 && x < grid[0].length)
50
+ grid[y][x] = Math.max(grid[y][x], type);
51
+ }
52
+ function bresenham(grid, x0, y0, x1, y1, type) {
53
+ const dx = Math.abs(x1 - x0), dy = Math.abs(y1 - y0);
54
+ const sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1;
55
+ let err = dx - dy, x = x0, y = y0;
56
+ while (true) {
57
+ plot(grid, x, y, type);
58
+ if (x === x1 && y === y1)
59
+ break;
60
+ const e2 = 2 * err;
61
+ if (e2 > -dy) {
62
+ err -= dy;
63
+ x += sx;
64
+ }
65
+ if (e2 < dx) {
66
+ err += dx;
67
+ y += sy;
68
+ }
69
+ }
70
+ }
71
+ function fillCircle(grid, cx, cy, r, type) {
72
+ for (let y = Math.floor(cy - r); y <= Math.ceil(cy + r); y++)
73
+ for (let x = Math.floor(cx - r); x <= Math.ceil(cx + r); x++)
74
+ if ((x - cx) ** 2 + (y - cy) ** 2 <= r * r)
75
+ plot(grid, x, y, type);
76
+ }
77
+ // Type: 1=spoke, 2=clean node, 3=center hub, 4=threat node (NE)
78
+ const LOGO_DATA = (() => {
79
+ const W = 22, H = 28;
80
+ const grid = Array.from({ length: H }, () => Array(W).fill(0));
81
+ const cx = 11, cy = 14, R = 10;
82
+ const angles = [270, 315, 0, 45, 90, 135, 180, 225]; // N, NE, E, SE, S, SW, W, NW
83
+ const nodes = angles.map(a => {
84
+ const rad = a * Math.PI / 180;
85
+ return [Math.round(cx + R * Math.cos(rad)), Math.round(cy + R * Math.sin(rad))];
86
+ });
87
+ // Spokes first (lowest priority)
88
+ for (const [nx, ny] of nodes)
89
+ bresenham(grid, cx, cy, nx, ny, 1);
90
+ // Outer nodes (higher priority overwrites spoke pixels)
91
+ nodes.forEach(([nx, ny], i) => fillCircle(grid, nx, ny, 1.5, i === 1 ? 4 : 2));
92
+ // Center hub (highest non-threat priority)
93
+ fillCircle(grid, cx, cy, 3, 3);
94
+ // Convert pixel grid to braille characters + per-char type
95
+ const chars = [];
96
+ const types = [];
97
+ for (let row = 0; row < H; row += 4) {
98
+ for (let col = 0; col < W; col += 2) {
99
+ let bits = 0, maxType = 0;
100
+ const offsets = [
101
+ [0, 0, 1], [1, 0, 2], [2, 0, 4], [3, 0, 64],
102
+ [0, 1, 8], [1, 1, 16], [2, 1, 32], [3, 1, 128],
103
+ ];
104
+ for (const [dy, dx, bit] of offsets) {
105
+ const py = row + dy, px = col + dx;
106
+ if (py < H && px < W && grid[py][px] > 0) {
107
+ bits |= bit;
108
+ maxType = Math.max(maxType, grid[py][px]);
109
+ }
110
+ }
111
+ chars.push(String.fromCharCode(0x2800 + bits));
112
+ types.push(maxType);
113
+ }
114
+ }
115
+ return { chars, types, cols: W / 2 };
116
+ })();
117
+ function renderLogo(action) {
118
+ const colors = {
119
+ 1: chalk_1.default.dim,
120
+ 2: chalk_1.default.white,
121
+ 3: (s) => chalk_1.default.bold.white(s),
122
+ 4: action === "block" ? chalk_1.default.red : action === "warn" ? chalk_1.default.yellow : chalk_1.default.green,
123
+ };
124
+ const result = [];
125
+ const { chars, types, cols } = LOGO_DATA;
126
+ for (let i = 0; i < chars.length; i += cols) {
127
+ let row = "";
128
+ for (let j = 0; j < cols; j++) {
129
+ const ch = chars[i + j];
130
+ const t = types[i + j];
131
+ row += t > 0 ? (colors[t] ?? chalk_1.default.dim)(ch) : ch;
132
+ }
133
+ result.push(row);
134
+ }
135
+ return result;
136
+ }
137
+ const ScoreHeader = ({ score, action, total, flagged, clean, severityCounts, userStatus, scanUsage, }) => {
138
+ const logo = renderLogo(action);
139
+ const showLogo = (process.stdout.columns ?? 80) >= 60;
140
+ return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", borderStyle: "round", borderColor: action === "block" ? "red" : action === "warn" ? "yellow" : "green", paddingLeft: 1, paddingRight: 1, width: "100%", children: (0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", flexGrow: 1, children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, children: [chalk_1.default.cyan("\u25C6"), " Dependency Guardian ", userStatus ? chalk_1.default.dim(userStatus) : ""] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.dim("Score"), " ", scoreColor(score, action)] }), total !== undefined && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: " " }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [chalk_1.default.dim(`${total} package${total !== 1 ? "s" : ""} scanned`), flagged !== undefined && flagged > 0 ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" ", chalk_1.default.yellow(`${flagged} flagged`), " ", chalk_1.default.green(`${clean ?? 0} clean`)] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" ", chalk_1.default.green("all clean")] }))] }), severityCounts && Object.values(severityCounts).some(v => v > 0) && ((0, jsx_runtime_1.jsxs)(ink_1.Box, { justifyContent: "space-between", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: severityBar(severityCounts) }), scanUsage && (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: scanUsage })] })), (!severityCounts || !Object.values(severityCounts).some(v => v > 0)) && scanUsage && ((0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: scanUsage }))] }))] }), showLogo && ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", marginLeft: 2, children: logo.map((line, i) => (0, jsx_runtime_1.jsx)(ink_1.Text, { children: line }, i)) }))] }) }));
141
+ };
142
+ exports.ScoreHeader = ScoreHeader;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SetupBanner = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const ink_1 = require("ink");
6
+ // Visible alert shown above the scan results when the user's DG setup has
7
+ // gaps — missing api key, missing pre-commit hook, etc. Renders nothing
8
+ // when issues is empty so it's free to drop in unconditionally.
9
+ //
10
+ // Yellow border, single line per issue, fix command in cyan so the user
11
+ // can copy-paste it.
12
+ const SetupBanner = ({ issues }) => {
13
+ if (issues.length === 0)
14
+ return null;
15
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingLeft: 1, paddingRight: 1, width: "100%", children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "! Setup incomplete" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { dimColor: true, children: [" \u2014 ", issues.length === 1 ? "1 thing" : `${issues.length} things`, " you haven't set up yet"] })] }), issues.map((issue) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " \u00B7 " }), issue.label, (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: " \u2192 " }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: issue.fix })] }, issue.id)))] }));
16
+ };
17
+ exports.SetupBanner = SetupBanner;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Spinner = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const ink_1 = require("ink");
9
+ const ink_spinner_1 = __importDefault(require("ink-spinner"));
10
+ const Spinner = ({ label }) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", children: (0, jsx_runtime_1.jsx)(ink_spinner_1.default, { type: "dots" }) }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [" ", label] })] }));
11
+ exports.Spinner = Spinner;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useExpandAnimation = useExpandAnimation;
4
+ const react_1 = require("react");
5
+ function useExpandAnimation(targetHeight, active, durationMs = 180) {
6
+ const [visibleLines, setVisibleLines] = (0, react_1.useState)(0);
7
+ const timerRef = (0, react_1.useRef)(null);
8
+ (0, react_1.useEffect)(() => {
9
+ if (timerRef.current) {
10
+ clearInterval(timerRef.current);
11
+ timerRef.current = null;
12
+ }
13
+ if (!active || targetHeight <= 0) {
14
+ setVisibleLines(0);
15
+ return;
16
+ }
17
+ // Reveal one line per tick
18
+ const intervalMs = Math.max(16, Math.floor(durationMs / targetHeight));
19
+ let current = 1;
20
+ setVisibleLines(1);
21
+ timerRef.current = setInterval(() => {
22
+ current++;
23
+ if (current >= targetHeight) {
24
+ setVisibleLines(targetHeight);
25
+ if (timerRef.current)
26
+ clearInterval(timerRef.current);
27
+ timerRef.current = null;
28
+ }
29
+ else {
30
+ setVisibleLines(current);
31
+ }
32
+ }, intervalMs);
33
+ return () => {
34
+ if (timerRef.current) {
35
+ clearInterval(timerRef.current);
36
+ timerRef.current = null;
37
+ }
38
+ };
39
+ }, [active, targetHeight, durationMs]);
40
+ return {
41
+ visibleLines: active ? visibleLines : 0,
42
+ isAnimating: active && visibleLines > 0 && visibleLines < targetHeight,
43
+ };
44
+ }