@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,141 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initTelemetry = initTelemetry;
37
+ exports.redact = redact;
38
+ exports.captureError = captureError;
39
+ exports.flush = flush;
40
+ /**
41
+ * Anonymous crash reporting via Sentry.
42
+ *
43
+ * Sends: error message, stack trace, Node version, OS, CLI version, command name.
44
+ * Never sends: package names, file paths, API keys, lockfile contents.
45
+ * Disable: DG_TELEMETRY=0 or DO_NOT_TRACK=1
46
+ */
47
+ const Sentry = __importStar(require("@sentry/node"));
48
+ // Sentry DSNs are public ingest endpoints, not secrets.
49
+ const CLI_DSN = "https://0b48c008cee0061b41bd45029fac2342@o4510904601935872.ingest.us.sentry.io/4511197701931008";
50
+ let initialized = false;
51
+ function initTelemetry(version) {
52
+ if (process.env.DG_TELEMETRY === "0" || process.env.DO_NOT_TRACK === "1")
53
+ return;
54
+ try {
55
+ Sentry.init({
56
+ dsn: CLI_DSN,
57
+ release: `dg-cli@${version}`,
58
+ environment: process.env.CI ? "ci" : "local",
59
+ // TODO: reduce sample rate when CLI usage exceeds ~10k DAU
60
+ sampleRate: 1.0,
61
+ sendDefaultPii: false,
62
+ beforeSend(event) {
63
+ // Drop errors hitting RFC 2606 reserved hostnames (negative-path tests)
64
+ if (event.exception?.values?.some(ex => ex.value && /\.(invalid|test|example|localhost)\b/i.test(ex.value))) {
65
+ return null;
66
+ }
67
+ if (event.request) {
68
+ delete event.request.headers;
69
+ delete event.request.data;
70
+ delete event.request.cookies;
71
+ }
72
+ if (event.breadcrumbs) {
73
+ event.breadcrumbs = event.breadcrumbs.map(b => {
74
+ if (b.message)
75
+ b.message = redact(b.message);
76
+ if (b.data) {
77
+ for (const key of Object.keys(b.data)) {
78
+ if (typeof b.data[key] === "string") {
79
+ b.data[key] = redact(b.data[key]);
80
+ }
81
+ }
82
+ }
83
+ return b;
84
+ });
85
+ }
86
+ if (event.exception?.values) {
87
+ for (const ex of event.exception.values) {
88
+ if (ex.value)
89
+ ex.value = redact(ex.value);
90
+ if (ex.stacktrace?.frames) {
91
+ for (const frame of ex.stacktrace.frames) {
92
+ if (frame.filename) {
93
+ frame.filename = frame.filename
94
+ .replace(/^\/Users\/[^/]+/, "[HOME]")
95
+ .replace(/^\/home\/[^/]+/, "[HOME]")
96
+ .replace(/^C:\\Users\\[^\\]+/, "[HOME]");
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ return event;
103
+ },
104
+ });
105
+ initialized = true;
106
+ }
107
+ catch {
108
+ // Sentry init failure should never block the CLI
109
+ }
110
+ }
111
+ // Redact secrets before sending to Sentry. Intentionally broad on API keys —
112
+ // tighter bounds like {8,} would miss short dev/test tokens and leak them.
113
+ // Being greedy here is the right default.
114
+ function redact(s) {
115
+ return s
116
+ .replace(/dg_(?:live|test)_[A-Za-z0-9_-]{3,}/gi, "[DG_KEY_REDACTED]")
117
+ .replace(/\/Users\/[^\s:]+/g, "[USER_PATH]")
118
+ .replace(/\/home\/[^\s:]+/g, "[USER_PATH]")
119
+ .replace(/C:\\Users\\[^\s:]+/g, "[USER_PATH]")
120
+ .replace(/Bearer [^\s]+/g, "Bearer [REDACTED]");
121
+ }
122
+ function captureError(error, context) {
123
+ if (!initialized)
124
+ return;
125
+ try {
126
+ if (context)
127
+ Sentry.setContext("scan", context);
128
+ Sentry.captureException(error);
129
+ }
130
+ catch {
131
+ // Silent
132
+ }
133
+ }
134
+ async function flush() {
135
+ if (!initialized)
136
+ return;
137
+ try {
138
+ await Sentry.flush(2000);
139
+ }
140
+ catch { /* ignore — best-effort */ }
141
+ }
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.App = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const ink_1 = require("ink");
7
+ const auth_1 = require("../auth");
8
+ const useScan_1 = require("./hooks/useScan");
9
+ const Spinner_1 = require("./components/Spinner");
10
+ const ProgressBar_1 = require("./components/ProgressBar");
11
+ const InteractiveResultsView_1 = require("./components/InteractiveResultsView");
12
+ const ErrorView_1 = require("./components/ErrorView");
13
+ const ProjectSelector_1 = require("./components/ProjectSelector");
14
+ const SetupBanner_1 = require("./components/SetupBanner");
15
+ const useTerminalSize_1 = require("./hooks/useTerminalSize");
16
+ const App = ({ config, userStatus, scanUsage, setupIssues = [] }) => {
17
+ const { state, scanSelectedProjects, restartSelection } = (0, useScan_1.useScan)(config);
18
+ const { exit } = (0, ink_1.useApp)();
19
+ (0, useTerminalSize_1.useTerminalSize)(); // keep hook active for resize-triggered re-renders
20
+ const prevPhaseRef = (0, react_1.useRef)(state.phase);
21
+ const altScreenActiveRef = (0, react_1.useRef)(false);
22
+ // Enter alternate screen buffer for the entire app lifecycle
23
+ (0, react_1.useEffect)(() => {
24
+ if (!process.stdout.isTTY)
25
+ return;
26
+ process.stdout.write("\x1b[?1049h"); // enter alternate screen
27
+ process.stdout.write("\x1b[2J\x1b[H"); // clear + home
28
+ process.stdout.write("\x1b[?1003l"); // disable mouse all-events (prevents scroll → arrow key)
29
+ process.stdout.write("\x1b[?1000l"); // disable mouse click tracking
30
+ altScreenActiveRef.current = true;
31
+ return () => {
32
+ if (altScreenActiveRef.current) {
33
+ process.stdout.write("\x1b[?1049l"); // exit alternate screen
34
+ altScreenActiveRef.current = false;
35
+ }
36
+ process.stdout.write("\x1b[?25h"); // restore cursor
37
+ };
38
+ }, []);
39
+ // Track phase transitions (Ink handles repainting automatically)
40
+ (0, react_1.useEffect)(() => {
41
+ prevPhaseRef.current = state.phase;
42
+ }, [state.phase]);
43
+ const leaveAltScreen = (0, react_1.useCallback)(() => {
44
+ if (altScreenActiveRef.current && process.stdout.isTTY) {
45
+ process.stdout.write("\x1b[?1049l");
46
+ altScreenActiveRef.current = false;
47
+ }
48
+ process.stdout.write("\x1b[?25h");
49
+ }, []);
50
+ const handleResultsExit = (0, react_1.useCallback)(() => {
51
+ if (state.phase === "results") {
52
+ const { result } = state;
53
+ if (result.action === "block" && config.mode === "block") {
54
+ process.exitCode = 2;
55
+ }
56
+ else if (result.action === "block" || result.action === "warn") {
57
+ process.exitCode = 1;
58
+ }
59
+ else {
60
+ process.exitCode = 0;
61
+ }
62
+ }
63
+ leaveAltScreen();
64
+ exit();
65
+ }, [state, config, exit, leaveAltScreen]);
66
+ // Exit alt screen BEFORE writing messages so they appear on the main screen
67
+ const exitWithMessage = (0, react_1.useCallback)((message, exitCode) => {
68
+ process.exitCode = exitCode;
69
+ leaveAltScreen();
70
+ process.stderr.write(message);
71
+ return setTimeout(() => exit(), 0);
72
+ }, [exit, leaveAltScreen]);
73
+ (0, react_1.useEffect)(() => {
74
+ if (state.phase === "empty") {
75
+ const timer = exitWithMessage(`${state.message}\n`, 0);
76
+ return () => clearTimeout(timer);
77
+ }
78
+ if (state.phase === "error") {
79
+ const timer = exitWithMessage(`Error: ${state.error.message}\n`, 3);
80
+ return () => clearTimeout(timer);
81
+ }
82
+ if (state.phase === "trial_exhausted") {
83
+ let msg = "Free trial scans used up. Run `dg login` to create a free account and continue scanning.\n";
84
+ try {
85
+ if ((0, auth_1.getStoredApiKey)()) {
86
+ msg = "Your API key may be invalid or expired. Run `dg logout` then `dg login` to re-authenticate.\n";
87
+ }
88
+ }
89
+ catch { /* ignore — best-effort */ }
90
+ const timer = exitWithMessage(msg, 1);
91
+ return () => clearTimeout(timer);
92
+ }
93
+ }, [state, exitWithMessage]);
94
+ // Global q/ESC handler for phases that lack their own input handling
95
+ (0, ink_1.useInput)((input, key) => {
96
+ if (state.phase === "discovering" || state.phase === "scanning") {
97
+ if (input === "q" || key.escape) {
98
+ process.exitCode = 0;
99
+ leaveAltScreen();
100
+ exit();
101
+ }
102
+ }
103
+ });
104
+ const content = (() => {
105
+ switch (state.phase) {
106
+ case "discovering":
107
+ return (0, jsx_runtime_1.jsx)(Spinner_1.Spinner, { label: `Searching for dependencies in ${process.cwd()} ...` });
108
+ case "selecting":
109
+ return ((0, jsx_runtime_1.jsx)(ProjectSelector_1.ProjectSelector, { projects: state.projects, onConfirm: scanSelectedProjects, onCancel: () => { process.exitCode = 0; leaveAltScreen(); exit(); }, userStatus: userStatus }));
110
+ case "scanning":
111
+ return ((0, jsx_runtime_1.jsx)(ProgressBar_1.ProgressBar, { value: state.done, total: state.total, label: state.currentBatch.length > 0
112
+ ? state.currentBatch[state.currentBatch.length - 1]
113
+ : undefined }));
114
+ case "results":
115
+ return ((0, jsx_runtime_1.jsx)(InteractiveResultsView_1.InteractiveResultsView, { result: state.result, config: config, durationMs: state.durationMs, onExit: handleResultsExit, onBack: restartSelection ?? undefined, discoveredTotal: state.discoveredTotal, userStatus: userStatus, scanUsage: scanUsage }));
116
+ case "empty":
117
+ return (0, jsx_runtime_1.jsx)(ink_1.Text, { dimColor: true, children: state.message });
118
+ case "error":
119
+ return (0, jsx_runtime_1.jsx)(ErrorView_1.ErrorView, { error: state.error });
120
+ case "trial_exhausted": {
121
+ let hasKey = false;
122
+ try {
123
+ hasKey = !!(0, auth_1.getStoredApiKey)();
124
+ }
125
+ catch { /* ignore — best-effort */ }
126
+ return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", paddingLeft: 2, children: hasKey ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "Your API key may be invalid or expired." }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Run ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: "dg logout" }), " then ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: "dg login" }), " to re-authenticate."] })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", bold: true, children: "Free trial scans used up." }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: ["Run ", (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", bold: true, children: "dg login" }), " to create a free account and continue scanning."] })] })) }));
127
+ }
128
+ }
129
+ })();
130
+ // Show the setup banner above the results — but not during empty/error/
131
+ // trial_exhausted phases where it would compete with the message the user
132
+ // actually needs to read.
133
+ const showBanner = state.phase === "selecting" &&
134
+ setupIssues.length > 0;
135
+ return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [showBanner ? (0, jsx_runtime_1.jsx)(SetupBanner_1.SetupBanner, { issues: setupIssues }) : null, content] }));
136
+ };
137
+ exports.App = App;