@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13

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 (116) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/LICENSE.md +559 -186
  3. package/README.md +18 -0
  4. package/dist/bin.js +1 -9
  5. package/dist/config/index.d.ts +477 -556
  6. package/dist/config/index.js +1 -2
  7. package/dist/generate/index.js +1 -3
  8. package/dist/packem_chunks/applyDefaults.js +2 -336
  9. package/dist/packem_chunks/bin.js +234 -9552
  10. package/dist/packem_chunks/doctor-probe.js +2 -112
  11. package/dist/packem_chunks/fix.js +11 -234
  12. package/dist/packem_chunks/handler.js +1 -99
  13. package/dist/packem_chunks/handler10.js +2 -53
  14. package/dist/packem_chunks/handler11.js +1 -32
  15. package/dist/packem_chunks/handler12.js +5 -100
  16. package/dist/packem_chunks/handler13.js +1 -25
  17. package/dist/packem_chunks/handler14.js +18 -916
  18. package/dist/packem_chunks/handler15.js +15 -201
  19. package/dist/packem_chunks/handler16.js +1 -124
  20. package/dist/packem_chunks/handler17.js +1 -13
  21. package/dist/packem_chunks/handler18.js +1 -106
  22. package/dist/packem_chunks/handler19.js +1 -19
  23. package/dist/packem_chunks/handler2.js +2 -75
  24. package/dist/packem_chunks/handler20.js +5 -29
  25. package/dist/packem_chunks/handler21.js +1 -222
  26. package/dist/packem_chunks/handler22.js +1 -237
  27. package/dist/packem_chunks/handler23.js +5 -101
  28. package/dist/packem_chunks/handler24.js +1 -110
  29. package/dist/packem_chunks/handler25.js +3 -402
  30. package/dist/packem_chunks/handler26.js +1 -13
  31. package/dist/packem_chunks/handler27.js +1 -63
  32. package/dist/packem_chunks/handler28.js +7 -34
  33. package/dist/packem_chunks/handler29.js +21 -456
  34. package/dist/packem_chunks/handler3.js +4 -95
  35. package/dist/packem_chunks/handler30.js +3 -170
  36. package/dist/packem_chunks/handler31.js +1 -530
  37. package/dist/packem_chunks/handler32.js +2 -214
  38. package/dist/packem_chunks/handler33.js +25 -119
  39. package/dist/packem_chunks/handler34.js +2 -630
  40. package/dist/packem_chunks/handler35.js +3 -283
  41. package/dist/packem_chunks/handler36.js +22 -542
  42. package/dist/packem_chunks/handler37.js +410 -744
  43. package/dist/packem_chunks/handler38.js +22 -989
  44. package/dist/packem_chunks/handler39.js +22 -574
  45. package/dist/packem_chunks/handler4.js +2 -90
  46. package/dist/packem_chunks/handler40.js +22 -1685
  47. package/dist/packem_chunks/handler41.js +6 -1088
  48. package/dist/packem_chunks/handler42.js +5 -797
  49. package/dist/packem_chunks/handler43.js +10 -2658
  50. package/dist/packem_chunks/handler44.js +51 -3784
  51. package/dist/packem_chunks/handler45.js +25 -2574
  52. package/dist/packem_chunks/handler46.js +3 -3769
  53. package/dist/packem_chunks/handler47.js +21 -1485
  54. package/dist/packem_chunks/handler48.js +42 -0
  55. package/dist/packem_chunks/handler5.js +8 -174
  56. package/dist/packem_chunks/handler6.js +1 -95
  57. package/dist/packem_chunks/handler7.js +1 -115
  58. package/dist/packem_chunks/handler8.js +1 -12
  59. package/dist/packem_chunks/handler9.js +1 -29
  60. package/dist/packem_chunks/heal-accept.js +10 -522
  61. package/dist/packem_chunks/heal.js +14 -673
  62. package/dist/packem_chunks/index.js +7 -873
  63. package/dist/packem_chunks/loader.js +1 -23
  64. package/dist/packem_chunks/tar.js +3 -0
  65. package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
  66. package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
  67. package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
  68. package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
  69. package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
  70. package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
  71. package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
  72. package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
  73. package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
  74. package/dist/packem_shared/index-DH-5hsrC.js +1 -0
  75. package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
  76. package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
  77. package/dist/packem_shared/registry-CkubDdiY.js +2 -0
  78. package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
  79. package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
  80. package/dist/packem_shared/selectors-BylODRiM.js +3 -0
  81. package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
  82. package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
  83. package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
  84. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
  85. package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
  86. package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
  87. package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
  88. package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
  89. package/index.js +556 -727
  90. package/package.json +19 -29
  91. package/schemas/project.schema.json +739 -297
  92. package/schemas/vis-config.schema.json +3365 -384
  93. package/templates/buildkite-ci/template.yml +20 -20
  94. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
  95. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
  96. package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
  97. package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
  98. package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
  99. package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
  100. package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
  101. package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
  102. package/dist/packem_shared/docker-2iZzc280.js +0 -181
  103. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
  104. package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
  105. package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
  106. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
  107. package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
  108. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
  109. package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
  110. package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
  111. package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
  112. package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
  113. package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
  114. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
  115. package/dist/packem_shared/utils-CthVdBPS.js +0 -40
  116. package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
@@ -1,989 +1,22 @@
1
- import { createRequire as __cjs_createRequire } from "node:module";
2
-
3
- const __cjs_require = __cjs_createRequire(import.meta.url);
4
-
5
- const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
-
7
- const __cjs_getBuiltinModule = (module) => {
8
- // Check if we're in Node.js and version supports getBuiltinModule
9
- if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
- const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
- // Node.js 20.16.0+ and 22.3.0+
12
- if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
- return __cjs_getProcess.getBuiltinModule(module);
14
- }
15
- }
16
- // Fallback to createRequire
17
- return __cjs_require(module);
18
- };
19
-
20
- const {
21
- writeFileSync
22
- } = __cjs_getBuiltinModule("node:fs");
23
- import { bold, cyan, dim } from '@visulima/colorize';
24
- import { projectGraphToDot } from '@visulima/task-runner';
25
- import { Box, Text, ScrollView, ScrollBar, useApp, useWindowSize, useInput, Dialog, render } from '@visulima/tui';
26
- import { _ as QuitDialog, d as discoverWorkspace, b as buildProjectGraph, i as isInCi } from './bin.js';
27
- import React, { useSyncExternalStore, useState, useRef, useMemo, useCallback, useEffect } from 'react';
28
- import { jsx, jsxs } from 'react/jsx-runtime';
29
-
30
- const buildNodes = (projectGraph) => {
31
- const reverseDeps = /* @__PURE__ */ new Map();
32
- for (const [source, deps] of Object.entries(projectGraph.dependencies)) {
33
- for (const dep of deps) {
34
- const existing = reverseDeps.get(dep.target) ?? [];
35
- existing.push(source);
36
- reverseDeps.set(dep.target, existing);
37
- }
38
- }
39
- return Object.values(projectGraph.nodes).map((node) => {
40
- return {
41
- deps: projectGraph.dependencies[node.name] ?? [],
42
- name: node.name,
43
- reverseDeps: reverseDeps.get(node.name) ?? [],
44
- type: node.type
45
- };
46
- }).sort((a, b) => {
47
- if (a.type !== b.type) {
48
- return a.type === "application" ? -1 : 1;
49
- }
50
- return a.name.localeCompare(b.name);
51
- });
52
- };
53
- const filterNodes = (allNodes, filterType, filterText) => {
54
- let filtered = allNodes;
55
- if (filterType === "app") {
56
- filtered = filtered.filter((n) => n.type === "application");
57
- } else if (filterType === "lib") {
58
- filtered = filtered.filter((n) => n.type !== "application");
59
- }
60
- if (filterText) {
61
- const lower = filterText.toLowerCase();
62
- filtered = filtered.filter((n) => n.name.toLowerCase().includes(lower));
63
- }
64
- return filtered;
65
- };
66
- class GraphStore {
67
- #state;
68
- #listeners = /* @__PURE__ */ new Set();
69
- #projectGraph;
70
- constructor(projectGraph) {
71
- this.#projectGraph = projectGraph;
72
- const allNodes = buildNodes(projectGraph);
73
- this.#state = {
74
- allNodes,
75
- filterActive: false,
76
- filterText: "",
77
- filterType: "all",
78
- focusedPanel: "list",
79
- selectedIndex: 0
80
- };
81
- }
82
- // ── React integration ───────────────────────────────────────────
83
- getSnapshot = () => this.#state;
84
- subscribe = (listener) => {
85
- this.#listeners.add(listener);
86
- return () => {
87
- this.#listeners.delete(listener);
88
- };
89
- };
90
- // ── Derived data ────────────────────────────────────────────────
91
- getFilteredNodes() {
92
- return filterNodes(this.#state.allNodes, this.#state.filterType, this.#state.filterText);
93
- }
94
- getStats() {
95
- const apps = this.#state.allNodes.filter((n) => n.type === "application").length;
96
- const total = this.#state.allNodes.length;
97
- const deps = Object.values(this.#projectGraph.dependencies).reduce((s, d) => s + d.length, 0);
98
- return { apps, deps, libs: total - apps, total };
99
- }
100
- // ── Navigation ──────────────────────────────────────────────────
101
- setSelectedIndex(index) {
102
- const filtered = this.getFilteredNodes();
103
- const clamped = filtered.length === 0 ? -1 : Math.max(0, Math.min(index, filtered.length - 1));
104
- if (clamped !== this.#state.selectedIndex) {
105
- this.#emit({ ...this.#state, selectedIndex: clamped });
106
- }
107
- }
108
- setFocusedPanel(panel) {
109
- if (panel !== this.#state.focusedPanel) {
110
- this.#emit({ ...this.#state, focusedPanel: panel });
111
- }
112
- }
113
- // ── Filtering ───────────────────────────────────────────────────
114
- setFilterType(type) {
115
- if (type !== this.#state.filterType) {
116
- this.#emit({
117
- ...this.#state,
118
- filterType: type,
119
- selectedIndex: 0
120
- });
121
- }
122
- }
123
- setFilter(text) {
124
- this.#emit({
125
- ...this.#state,
126
- filterText: text,
127
- selectedIndex: 0
128
- });
129
- }
130
- setFilterActive(active) {
131
- if (active !== this.#state.filterActive) {
132
- this.#emit({
133
- ...this.#state,
134
- filterActive: active,
135
- filterText: active ? this.#state.filterText : "",
136
- selectedIndex: active ? this.#state.selectedIndex : 0
137
- });
138
- }
139
- }
140
- // ── Internal ────────────────────────────────────────────────────
141
- #emit(newState) {
142
- this.#state = newState;
143
- for (const listener of this.#listeners) {
144
- try {
145
- listener();
146
- } catch {
147
- }
148
- }
149
- }
150
- }
151
-
152
- const ProjectDetailPanel = ({ focused, node, scrollRef }) => {
153
- const borderColor = focused ? "white" : "gray";
154
- if (!node) {
155
- return jsx(Box, { alignItems: "center", borderColor: "gray", borderStyle: "single", flexDirection: "column", flexGrow: 1, justifyContent: "center", children: jsx(Text, { dimColor: true, children: "No project selected" }) });
156
- }
157
- const isApp = node.type === "application";
158
- const typeColor = isApp ? "yellow" : "cyan";
159
- const typeLabel = isApp ? "Application" : "Library";
160
- return jsx(
161
- Box,
162
- {
163
- borderColor,
164
- borderStyle: "single",
165
- borderTopRightTitle: ` ${typeLabel} `,
166
- borderTopTitle: ` ${node.name} `,
167
- flexDirection: "column",
168
- flexGrow: 1,
169
- children: jsxs(ScrollView, { flexGrow: 1, flexShrink: 1, paddingX: 2, ref: scrollRef, scrollbar: true, scrollbarColor: "gray", children: [
170
- jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
171
- jsxs(Box, { children: [
172
- jsx(Text, { dimColor: true, children: "── " }),
173
- jsx(Text, { bold: true, color: "white", children: "DEPENDS ON" }),
174
- jsxs(Text, { dimColor: true, children: [
175
- " (",
176
- node.deps.length,
177
- ")"
178
- ] })
179
- ] }),
180
- node.deps.length === 0 ? jsx(Box, { marginTop: 1, paddingLeft: 2, children: jsx(Text, { dimColor: true, children: "No dependencies" }) }) : jsx(Box, { flexDirection: "column", marginTop: 1, children: node.deps.map((dep) => jsxs(Box, { gap: 1, paddingLeft: 2, children: [
181
- jsx(Text, { color: "cyan", children: "→" }),
182
- jsx(Text, { children: dep.target }),
183
- dep.type !== "static" && jsxs(Text, { dimColor: true, children: [
184
- "(",
185
- dep.type,
186
- ")"
187
- ] })
188
- ] }, dep.target)) })
189
- ] }),
190
- jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
191
- jsxs(Box, { children: [
192
- jsx(Text, { dimColor: true, children: "── " }),
193
- jsx(Text, { bold: true, color: "white", children: "REQUIRED BY" }),
194
- jsxs(Text, { dimColor: true, children: [
195
- " (",
196
- node.reverseDeps.length,
197
- ")"
198
- ] })
199
- ] }),
200
- node.reverseDeps.length === 0 ? jsx(Box, { marginTop: 1, paddingLeft: 2, children: jsx(Text, { dimColor: true, children: "No reverse dependencies" }) }) : jsx(Box, { flexDirection: "column", marginTop: 1, children: node.reverseDeps.map((rdep) => jsxs(Box, { gap: 1, paddingLeft: 2, children: [
201
- jsx(Text, { color: "magenta", children: "←" }),
202
- jsx(Text, { children: rdep })
203
- ] }, rdep)) })
204
- ] }),
205
- jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
206
- jsxs(Box, { children: [
207
- jsx(Text, { dimColor: true, children: "── " }),
208
- jsx(Text, { bold: true, color: "white", children: "INFO" })
209
- ] }),
210
- jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 2, children: [
211
- jsxs(Box, { children: [
212
- jsx(Box, { width: 16, children: jsx(Text, { dimColor: true, children: "Type:" }) }),
213
- jsx(Text, { color: typeColor, children: typeLabel })
214
- ] }),
215
- jsxs(Box, { children: [
216
- jsx(Box, { width: 16, children: jsx(Text, { dimColor: true, children: "Dependencies:" }) }),
217
- jsx(Text, { children: String(node.deps.length) })
218
- ] }),
219
- jsxs(Box, { children: [
220
- jsx(Box, { width: 16, children: jsx(Text, { dimColor: true, children: "Required by:" }) }),
221
- jsx(Text, { children: String(node.reverseDeps.length) })
222
- ] }),
223
- jsxs(Box, { children: [
224
- jsx(Box, { width: 16, children: jsx(Text, { dimColor: true, children: "Connectivity:" }) }),
225
- jsx(Text, { children: String(node.deps.length + node.reverseDeps.length) })
226
- ] })
227
- ] })
228
- ] })
229
- ] })
230
- }
231
- );
232
- };
233
-
234
- const FILTER_LABELS = [
235
- { key: "all", label: "All", shortcut: "1" },
236
- { key: "app", label: "Apps", shortcut: "2" },
237
- { key: "lib", label: "Libs", shortcut: "3" }
238
- ];
239
- const ProjectRow = ({ isSelected, node }) => {
240
- const isApp = node.type === "application";
241
- const typeColor = isApp ? "yellow" : "cyan";
242
- const typeLabel = isApp ? "app" : "lib";
243
- return jsxs(Box, { flexShrink: 0, height: 1, children: [
244
- jsxs(Text, { children: [
245
- isSelected ? "▶" : " ",
246
- " "
247
- ] }),
248
- jsx(Box, { flexGrow: 1, children: jsx(Text, { bold: isSelected, inverse: isSelected, wrap: "truncate", children: node.name }) }),
249
- jsxs(Text, { color: typeColor, children: [
250
- " ",
251
- typeLabel
252
- ] }),
253
- jsxs(Text, { dimColor: true, children: [
254
- " ",
255
- "→",
256
- node.deps.length,
257
- " ",
258
- "←",
259
- node.reverseDeps.length
260
- ] })
261
- ] });
262
- };
263
- const TypeHeader = ({ count, label }) => jsxs(Box, { flexShrink: 0, height: 1, marginTop: 1, children: [
264
- jsxs(Text, { dimColor: true, children: [
265
- "▼",
266
- " "
267
- ] }),
268
- jsx(Text, { bold: true, color: "white", children: label.toUpperCase() }),
269
- jsxs(Text, { dimColor: true, children: [
270
- " (",
271
- count,
272
- ")"
273
- ] })
274
- ] });
275
- const ProjectListPanel = ({
276
- filterActive,
277
- filterText,
278
- filterType,
279
- focused,
280
- nodes,
281
- scrollOffset,
282
- selectedIndex,
283
- stats,
284
- viewportHeight
285
- }) => {
286
- const borderColor = focused ? "white" : "gray";
287
- const apps = nodes.filter((n) => n.type === "application");
288
- const libs = nodes.filter((n) => n.type !== "application");
289
- const rows = [];
290
- let flatIndex = 0;
291
- if (apps.length > 0) {
292
- rows.push(jsx(TypeHeader, { count: apps.length, label: "Applications" }, "hdr-apps"));
293
- for (const node of apps) {
294
- const currentIndex = flatIndex;
295
- rows.push(jsx(ProjectRow, { isSelected: currentIndex === selectedIndex, node }, node.name));
296
- flatIndex++;
297
- }
298
- }
299
- if (libs.length > 0) {
300
- rows.push(jsx(TypeHeader, { count: libs.length, label: "Libraries" }, "hdr-libs"));
301
- for (const node of libs) {
302
- const currentIndex = flatIndex;
303
- rows.push(jsx(ProjectRow, { isSelected: currentIndex === selectedIndex, node }, node.name));
304
- flatIndex++;
305
- }
306
- }
307
- let contentHeight = 0;
308
- if (apps.length > 0) {
309
- contentHeight += 2 + apps.length;
310
- }
311
- if (libs.length > 0) {
312
- contentHeight += 2 + libs.length;
313
- }
314
- const showScrollbar = contentHeight > viewportHeight && viewportHeight > 0;
315
- return jsxs(Box, { borderColor, borderStyle: "single", flexDirection: "column", flexGrow: 1, children: [
316
- jsxs(Box, { flexShrink: 0, gap: 1, paddingX: 1, children: [
317
- jsx(Text, { bold: true, inverse: true, children: " VIS " }),
318
- jsxs(Text, { wrap: "truncate", children: [
319
- stats.total,
320
- " packages"
321
- ] }),
322
- jsxs(Text, { dimColor: true, children: [
323
- "(",
324
- stats.apps,
325
- " apps, ",
326
- stats.libs,
327
- " libs, ",
328
- stats.deps,
329
- " deps)"
330
- ] })
331
- ] }),
332
- jsx(Box, { flexShrink: 0, gap: 1, paddingX: 1, paddingY: 1, children: FILTER_LABELS.map((f) => {
333
- const isActive = filterType === f.key;
334
- return jsxs(Box, { children: [
335
- jsx(Text, { dimColor: !isActive, children: "[" }),
336
- jsx(Text, { bold: isActive, color: isActive ? "cyan" : "gray", children: f.shortcut }),
337
- jsx(Text, { dimColor: !isActive, children: "]" }),
338
- jsxs(Text, { color: isActive ? "white" : "gray", children: [
339
- " ",
340
- f.label
341
- ] })
342
- ] }, f.key);
343
- }) }),
344
- filterActive && jsxs(Box, { flexShrink: 0, paddingX: 1, children: [
345
- jsx(Text, { bold: true, color: "white", children: "/ " }),
346
- jsx(Text, { children: filterText }),
347
- jsx(Text, { inverse: true, children: " " })
348
- ] }),
349
- jsxs(Box, { flexDirection: "row", flexGrow: 1, overflow: "hidden", children: [
350
- jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", paddingLeft: 1, children: jsx(Box, { flexDirection: "column", marginTop: -scrollOffset, children: rows }) }),
351
- showScrollbar && jsx(Box, { flexShrink: 0, marginLeft: 1, marginRight: 1, children: jsx(ScrollBar, { contentHeight, placement: "inset", scrollOffset, style: "block", viewportHeight }) })
352
- ] })
353
- ] });
354
- };
355
-
356
- const MIN_HORIZONTAL_WIDTH = 100;
357
- const MIN_VIEWPORT_WIDTH = 40;
358
- const MIN_VIEWPORT_HEIGHT = 10;
359
- const FILTER_KEYS = {
360
- 1: "all",
361
- 2: "app",
362
- 3: "lib"
363
- };
364
- const VisGraphApp = ({ autoExitSeconds = 0, store }) => {
365
- const { exit } = useApp();
366
- const { columns, rows } = useWindowSize();
367
- const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
368
- const [helpVisible, setHelpVisible] = useState(false);
369
- const helpScrollRef = useRef(null);
370
- const detailScrollRef = useRef(null);
371
- const [listScrollOffset, setListScrollOffset] = useState(0);
372
- const [quitDialogVisible, setQuitDialogVisible] = useState(false);
373
- const filteredNodes = useMemo(() => store.getFilteredNodes(), [state.allNodes, state.filterType, state.filterText]);
374
- const stats = useMemo(() => store.getStats(), [state.allNodes]);
375
- const selectedNode = filteredNodes[state.selectedIndex] ?? null;
376
- const getRowForIndex = useCallback(
377
- (index) => {
378
- const apps = filteredNodes.filter((n) => n.type === "application");
379
- const libs = filteredNodes.filter((n) => n.type !== "application");
380
- let row = 0;
381
- let count = 0;
382
- if (apps.length > 0) {
383
- row += 2;
384
- for (let i = 0; i < apps.length; i++) {
385
- if (count === index) {
386
- return row;
387
- }
388
- row += 1;
389
- count++;
390
- }
391
- }
392
- if (libs.length > 0) {
393
- row += 2;
394
- for (let i = 0; i < libs.length; i++) {
395
- if (count === index) {
396
- return row;
397
- }
398
- row += 1;
399
- count++;
400
- }
401
- }
402
- return row;
403
- },
404
- [filteredNodes]
405
- );
406
- const listViewportHeight = Math.max(1, rows - 8 - (state.filterActive ? 1 : 0));
407
- const scrollToIndex = useCallback(
408
- (index) => {
409
- const targetRow = getRowForIndex(index);
410
- setListScrollOffset((current) => {
411
- if (targetRow > current + listViewportHeight - 2) {
412
- return Math.max(0, targetRow - listViewportHeight + 2);
413
- }
414
- if (targetRow < current + 1) {
415
- return Math.max(0, targetRow - 1);
416
- }
417
- return current;
418
- });
419
- },
420
- [getRowForIndex, listViewportHeight]
421
- );
422
- useEffect(() => {
423
- detailScrollRef.current?.scrollToTop();
424
- }, [selectedNode?.name]);
425
- useInput(
426
- (input, key) => {
427
- if (input === "c" && key.ctrl) {
428
- exit();
429
- return;
430
- }
431
- if (quitDialogVisible) {
432
- return;
433
- }
434
- if (helpVisible) {
435
- if (key.escape || input === "?") {
436
- setHelpVisible(false);
437
- } else if (input === "q") {
438
- setHelpVisible(false);
439
- setQuitDialogVisible(true);
440
- } else if (key.downArrow || input === "j") {
441
- helpScrollRef.current?.scrollBy(1);
442
- } else if (key.upArrow || input === "k") {
443
- helpScrollRef.current?.scrollBy(-1);
444
- }
445
- return;
446
- }
447
- if (input === "?") {
448
- setHelpVisible(true);
449
- return;
450
- }
451
- if (input === "q") {
452
- setQuitDialogVisible(true);
453
- return;
454
- }
455
- if (key.tab) {
456
- store.setFocusedPanel(state.focusedPanel === "list" ? "detail" : "list");
457
- return;
458
- }
459
- if (FILTER_KEYS[input]) {
460
- store.setFilterType(FILTER_KEYS[input]);
461
- return;
462
- }
463
- if (state.filterActive) {
464
- if (key.escape) {
465
- store.setFilterActive(false);
466
- return;
467
- }
468
- if (key.return) {
469
- store.setFilterActive(false);
470
- return;
471
- }
472
- if (key.backspace) {
473
- store.setFilter(state.filterText.slice(0, -1));
474
- return;
475
- }
476
- if (input && !key.ctrl && !key.meta) {
477
- store.setFilter(state.filterText + input);
478
- return;
479
- }
480
- return;
481
- }
482
- if (state.focusedPanel === "list") {
483
- if (filteredNodes.length === 0) {
484
- if (input === "/") {
485
- store.setFilterActive(true);
486
- }
487
- return;
488
- }
489
- if (key.downArrow || input === "j") {
490
- const next = Math.min(state.selectedIndex + 1, filteredNodes.length - 1);
491
- store.setSelectedIndex(next);
492
- scrollToIndex(next);
493
- return;
494
- }
495
- if (key.upArrow || input === "k") {
496
- const next = Math.max(state.selectedIndex - 1, 0);
497
- store.setSelectedIndex(next);
498
- scrollToIndex(next);
499
- return;
500
- }
501
- if (key.pageDown) {
502
- const next = Math.min(state.selectedIndex + 10, filteredNodes.length - 1);
503
- store.setSelectedIndex(next);
504
- scrollToIndex(next);
505
- return;
506
- }
507
- if (key.pageUp) {
508
- const next = Math.max(state.selectedIndex - 10, 0);
509
- store.setSelectedIndex(next);
510
- scrollToIndex(next);
511
- return;
512
- }
513
- if (key.home) {
514
- store.setSelectedIndex(0);
515
- setListScrollOffset(0);
516
- return;
517
- }
518
- if (key.end) {
519
- const last = filteredNodes.length - 1;
520
- store.setSelectedIndex(last);
521
- scrollToIndex(last);
522
- return;
523
- }
524
- if (input === "/") {
525
- store.setFilterActive(true);
526
- return;
527
- }
528
- if (key.rightArrow) {
529
- store.setFocusedPanel("detail");
530
- return;
531
- }
532
- return;
533
- }
534
- if (state.focusedPanel === "detail") {
535
- if (key.escape || key.leftArrow) {
536
- store.setFocusedPanel("list");
537
- return;
538
- }
539
- if (key.downArrow || input === "j") {
540
- detailScrollRef.current?.scrollBy(1);
541
- return;
542
- }
543
- if (key.upArrow || input === "k") {
544
- detailScrollRef.current?.scrollBy(-1);
545
- return;
546
- }
547
- if (key.pageDown) {
548
- detailScrollRef.current?.scrollBy(10);
549
- return;
550
- }
551
- if (key.pageUp) {
552
- detailScrollRef.current?.scrollBy(-10);
553
- return;
554
- }
555
- if (key.home) {
556
- detailScrollRef.current?.scrollToTop();
557
- return;
558
- }
559
- if (key.end) {
560
- detailScrollRef.current?.scrollToBottom();
561
- }
562
- }
563
- },
564
- { isActive: true }
565
- );
566
- if (columns < MIN_VIEWPORT_WIDTH || rows < MIN_VIEWPORT_HEIGHT) {
567
- return jsx(Box, { alignItems: "center", height: rows, justifyContent: "center", width: columns, children: jsxs(Text, { color: "yellow", children: [
568
- "Terminal too small (",
569
- columns,
570
- "x",
571
- rows,
572
- ")"
573
- ] }) });
574
- }
575
- const isHorizontal = columns >= MIN_HORIZONTAL_WIDTH;
576
- const footer = jsx(Box, { borderBottom: false, borderColor: "gray", borderLeft: false, borderRight: false, borderStyle: "single", flexShrink: 0, children: jsxs(Box, { flexWrap: "wrap", gap: 2, paddingX: 1, children: [
577
- jsxs(Box, { gap: 1, children: [
578
- jsx(Text, { bold: true, color: "white", children: "q" }),
579
- jsx(Text, { dimColor: true, children: "QUIT" })
580
- ] }, "q"),
581
- jsxs(Box, { gap: 1, children: [
582
- jsx(Text, { bold: true, color: "white", children: "?" }),
583
- jsx(Text, { dimColor: true, children: "HELP" })
584
- ] }, "?"),
585
- jsxs(Box, { gap: 1, children: [
586
- jsx(Text, { bold: true, color: "white", children: "↑↓" }),
587
- jsx(Text, { dimColor: true, children: "NAV" })
588
- ] }, "nav"),
589
- jsxs(Box, { gap: 1, children: [
590
- jsx(Text, { bold: true, color: "white", children: "1-3 /" }),
591
- jsx(Text, { dimColor: true, children: "FILTER" })
592
- ] }, "f"),
593
- jsxs(Box, { gap: 1, children: [
594
- jsx(Text, { bold: true, color: "white", children: "Tab" }),
595
- jsx(Text, { dimColor: true, children: "PANEL" })
596
- ] }, "t")
597
- ] }) });
598
- const helpPopup = jsxs(
599
- Dialog,
600
- {
601
- footer: jsxs(Text, { dimColor: true, children: [
602
- jsx(Text, { bold: true, color: "white", children: "↑↓" }),
603
- " ",
604
- "scroll",
605
- " ",
606
- jsx(Text, { bold: true, color: "white", children: "?" }),
607
- "/",
608
- jsx(Text, { bold: true, color: "white", children: "Esc" }),
609
- " ",
610
- "close"
611
- ] }),
612
- scrollRef: helpScrollRef,
613
- title: "KEYBOARD SHORTCUTS",
614
- visible: helpVisible,
615
- width: 52,
616
- children: [
617
- jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
618
- jsxs(Box, { marginBottom: 1, children: [
619
- jsx(Text, { dimColor: true, children: "── " }),
620
- jsx(Text, { bold: true, color: "white", children: "NAVIGATION" })
621
- ] }),
622
- jsxs(Box, { children: [
623
- jsx(Box, { width: 24, children: jsxs(Text, { children: [
624
- jsxs(Text, { bold: true, color: "white", children: [
625
- " ",
626
- "↑",
627
- "/k"
628
- ] }),
629
- jsx(Text, { dimColor: true, children: " Move up" })
630
- ] }) }),
631
- jsxs(Text, { children: [
632
- jsxs(Text, { bold: true, color: "white", children: [
633
- " ",
634
- "↓",
635
- "/j"
636
- ] }),
637
- jsx(Text, { dimColor: true, children: " Move down" })
638
- ] })
639
- ] }),
640
- jsxs(Text, { children: [
641
- jsxs(Text, { bold: true, color: "white", children: [
642
- " ",
643
- "Tab"
644
- ] }),
645
- jsx(Text, { dimColor: true, children: " Switch panel" })
646
- ] }),
647
- jsxs(Text, { children: [
648
- jsxs(Text, { bold: true, color: "white", children: [
649
- " ",
650
- "→",
651
- "/",
652
- "←"
653
- ] }),
654
- jsx(Text, { dimColor: true, children: " Focus detail/list" })
655
- ] }),
656
- jsxs(Text, { children: [
657
- jsxs(Text, { bold: true, color: "white", children: [
658
- " ",
659
- "PgUp/PgDn"
660
- ] }),
661
- jsx(Text, { dimColor: true, children: " Jump 10 items" })
662
- ] }),
663
- jsxs(Text, { children: [
664
- jsxs(Text, { bold: true, color: "white", children: [
665
- " ",
666
- "Home/End"
667
- ] }),
668
- jsx(Text, { dimColor: true, children: " Jump to start/end" })
669
- ] })
670
- ] }),
671
- jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
672
- jsxs(Box, { marginBottom: 1, children: [
673
- jsx(Text, { dimColor: true, children: "── " }),
674
- jsx(Text, { bold: true, color: "white", children: "FILTERS" })
675
- ] }),
676
- jsxs(Box, { children: [
677
- jsx(Box, { width: 24, children: jsxs(Text, { children: [
678
- jsxs(Text, { bold: true, color: "white", children: [
679
- " ",
680
- "1"
681
- ] }),
682
- jsx(Text, { dimColor: true, children: " All" })
683
- ] }) }),
684
- jsxs(Text, { children: [
685
- jsxs(Text, { bold: true, color: "white", children: [
686
- " ",
687
- "2"
688
- ] }),
689
- jsx(Text, { dimColor: true, children: " Apps only" })
690
- ] })
691
- ] }),
692
- jsxs(Text, { children: [
693
- jsxs(Text, { bold: true, color: "white", children: [
694
- " ",
695
- "3"
696
- ] }),
697
- jsx(Text, { dimColor: true, children: " Libraries only" })
698
- ] }),
699
- jsxs(Text, { children: [
700
- jsxs(Text, { bold: true, color: "white", children: [
701
- " ",
702
- "/"
703
- ] }),
704
- jsx(Text, { dimColor: true, children: " Text filter" })
705
- ] })
706
- ] }),
707
- jsxs(Box, { flexDirection: "column", children: [
708
- jsxs(Box, { marginBottom: 1, children: [
709
- jsx(Text, { dimColor: true, children: "── " }),
710
- jsx(Text, { bold: true, color: "white", children: "ACTIONS" })
711
- ] }),
712
- jsxs(Text, { children: [
713
- jsxs(Text, { bold: true, color: "white", children: [
714
- " ",
715
- "q"
716
- ] }),
717
- jsx(Text, { dimColor: true, children: " Quit" })
718
- ] }),
719
- jsxs(Text, { children: [
720
- jsxs(Text, { bold: true, color: "white", children: [
721
- " ",
722
- "?"
723
- ] }),
724
- jsx(Text, { dimColor: true, children: " Toggle help" })
725
- ] })
726
- ] })
727
- ]
728
- }
729
- );
730
- const listPanel = jsx(
731
- ProjectListPanel,
732
- {
733
- filterActive: state.filterActive,
734
- filterText: state.filterText,
735
- filterType: state.filterType,
736
- focused: state.focusedPanel === "list",
737
- nodes: filteredNodes,
738
- scrollOffset: listScrollOffset,
739
- selectedIndex: state.selectedIndex,
740
- stats,
741
- viewportHeight: listViewportHeight
742
- }
743
- );
744
- const detailPanel = jsx(ProjectDetailPanel, { focused: state.focusedPanel === "detail", node: selectedNode, scrollRef: detailScrollRef });
745
- if (isHorizontal) {
746
- const detailWidth = Math.floor(columns * 0.35);
747
- return jsxs(Box, { flexDirection: "column", height: rows, width: columns, children: [
748
- jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [
749
- jsx(Box, { flexGrow: 1, children: listPanel }),
750
- jsx(Box, { width: detailWidth, children: detailPanel })
751
- ] }),
752
- footer,
753
- jsx(
754
- QuitDialog,
755
- {
756
- autoExitSeconds: autoExitSeconds ?? 3,
757
- onCancel: () => {
758
- setQuitDialogVisible(false);
759
- },
760
- visible: quitDialogVisible
761
- }
762
- ),
763
- helpPopup
764
- ] });
765
- }
766
- const listHeight = Math.floor(rows * 0.55);
767
- return jsxs(Box, { flexDirection: "column", height: rows, width: columns, children: [
768
- jsx(Box, { height: listHeight, children: listPanel }),
769
- jsx(Box, { flexGrow: 1, children: detailPanel }),
770
- footer,
771
- jsx(
772
- QuitDialog,
773
- {
774
- autoExitSeconds: autoExitSeconds ?? 3,
775
- onCancel: () => {
776
- setQuitDialogVisible(false);
777
- },
778
- visible: quitDialogVisible
779
- }
780
- ),
781
- helpPopup
782
- ] });
783
- };
784
-
785
- var __freeze = Object.freeze;
786
- var __defProp = Object.defineProperty;
787
- var __template = (cooked, raw) => __freeze(__defProp(cooked, "raw", { value: __freeze(raw || cooked.slice()) }));
788
- const printDepsTree = (name, prefix, isLast, nodes, printed, lines, maxDepth, currentDepth) => {
789
- const connector = isLast ? dim("└── ") : dim("├── ");
790
- const isDuplicate = printed.has(name);
791
- const suffix = isDuplicate ? dim(" (*)") : "";
792
- const node = nodes.get(name);
793
- const isApp = node?.type === "application";
794
- const displayName = isApp ? bold(name) : name;
795
- lines.push(`${prefix}${connector}${displayName}${suffix}`);
796
- if (isDuplicate) {
797
- return;
798
- }
799
- printed.add(name);
800
- const deps = node?.deps ?? [];
801
- const childPrefix = isLast ? `${prefix} ` : `${prefix}${dim("│")} `;
802
- if (currentDepth >= maxDepth && deps.length > 0) {
803
- lines.push(`${childPrefix}${dim(`... ${deps.length} more`)}`);
804
- return;
805
- }
806
- for (let i = 0; i < deps.length; i++) {
807
- const dep = deps[i];
808
- if (dep) {
809
- printDepsTree(dep.target, childPrefix, i === deps.length - 1, nodes, printed, lines, maxDepth, currentDepth + 1);
810
- }
811
- }
812
- };
813
- const printRootProject = (name, nodes, printed, lines, maxDepth, indent) => {
814
- const node = nodes.get(name);
815
- const isApp = node?.type === "application";
816
- const displayName = isApp ? bold(name) : name;
817
- lines.push(`${indent}${displayName}`);
818
- printed.add(name);
819
- const deps = node?.deps ?? [];
820
- if (deps.length === 0) {
821
- lines.push(`${indent} ${dim("(no dependencies)")}`);
822
- return;
823
- }
824
- if (maxDepth <= 0) {
825
- lines.push(`${indent} ${dim(`... ${deps.length} dependencies`)}`);
826
- return;
827
- }
828
- for (let i = 0; i < deps.length; i++) {
829
- const dep = deps[i];
830
- if (dep) {
831
- printDepsTree(dep.target, indent, i === deps.length - 1, nodes, printed, lines, maxDepth, 1);
832
- }
833
- }
834
- };
835
- const projectGraphToAscii = (projectGraph, maxDepth) => {
836
- const nodes = /* @__PURE__ */ new Map();
837
- for (const [name, node] of Object.entries(projectGraph.nodes)) {
838
- nodes.set(name, {
839
- deps: (projectGraph.dependencies[name] ?? []).map((d) => {
840
- return { target: d.target, type: d.type };
841
- }),
842
- name,
843
- type: node.type
844
- });
845
- }
846
- const apps = [];
847
- const libs = [];
848
- for (const [name, node] of nodes) {
849
- if (node.type === "application") {
850
- apps.push(name);
851
- } else {
852
- libs.push(name);
853
- }
854
- }
855
- apps.sort();
856
- libs.sort();
857
- const totalPackages = apps.length + libs.length;
858
- const totalDeps = Object.values(projectGraph.dependencies).reduce((sum, deps) => sum + deps.length, 0);
859
- const lines = [];
860
- lines.push(bold("Project Dependency Graph"), "");
861
- if (apps.length > 0) {
862
- lines.push(` ${bold(cyan(`Applications (${apps.length})`))}`, "");
863
- for (const name of apps) {
864
- const printed = /* @__PURE__ */ new Set();
865
- printRootProject(name, nodes, printed, lines, maxDepth, " ");
866
- lines.push("");
867
- }
868
- }
869
- if (libs.length > 0) {
870
- lines.push(` ${bold(cyan(`Libraries (${libs.length})`))}`, "");
871
- for (const name of libs) {
872
- const printed = /* @__PURE__ */ new Set();
873
- printRootProject(name, nodes, printed, lines, maxDepth, " ");
874
- lines.push("");
875
- }
876
- }
877
- const width = process.stdout.columns || 80;
878
- lines.push(dim("─".repeat(Math.min(width, 60))));
879
- lines.push(
880
- `${bold(String(totalPackages))} packages ${dim("·")} ${bold(String(totalDeps))} dependencies ${dim("·")} ${bold(String(apps.length))} apps${dim(",")} ${bold(String(libs.length))} libraries`
881
- );
882
- const allPrinted = /* @__PURE__ */ new Set();
883
- let hasDuplicates = false;
884
- for (const name of [...apps, ...libs]) {
885
- const deps = nodes.get(name)?.deps ?? [];
886
- for (const dep of deps) {
887
- if (allPrinted.has(dep.target)) {
888
- hasDuplicates = true;
889
- }
890
- allPrinted.add(dep.target);
891
- }
892
- allPrinted.add(name);
893
- }
894
- if (hasDuplicates) {
895
- lines.push(dim("(*) = already shown above"));
896
- }
897
- return lines.join("\n");
898
- };
899
- const projectGraphToJson = (projectGraph) => {
900
- const nodes = Object.values(projectGraph.nodes).map((node) => {
901
- return {
902
- name: node.name,
903
- type: node.type
904
- };
905
- });
906
- const edges = Object.values(projectGraph.dependencies).flat();
907
- return { edges, nodes };
908
- };
909
- var _a;
910
- const projectGraphToHtml = (projectGraph) => {
911
- const nodes = Object.values(projectGraph.nodes).map((node) => {
912
- return {
913
- name: node.name,
914
- type: node.type
915
- };
916
- });
917
- const edges = [];
918
- for (const deps of Object.values(projectGraph.dependencies)) {
919
- for (const dep of deps) {
920
- edges.push({ source: dep.source, target: dep.target, type: dep.type });
921
- }
922
- }
923
- const apps = nodes.filter((n) => n.type === "application");
924
- const libs = nodes.filter((n) => n.type !== "application");
925
- const graphData = { apps: apps.length, edges, libs: libs.length, nodes };
926
- return String.raw(_a || (_a = __template(['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Project Dependency Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; overflow: hidden; }\n svg { width: 100vw; height: 100vh; }\n .edge { fill: none; marker-end: url(#arrow); }\n .node rect { rx: 8; ry: 8; cursor: pointer; transition: stroke-width 0.15s; }\n .node text { font-size: 12px; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke-width: 2.5; stroke: #fff; }\n #info { position: fixed; top: 16px; right: 16px; background: #1e293b; padding: 14px 20px; border-radius: 10px; font-size: 13px; border: 1px solid #334155; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }\n #info b { font-variant-numeric: tabular-nums; }\n .app-count { color: #fbbf24; }\n .lib-count { color: #38bdf8; }\n .dep-count { color: #a78bfa; }\n #legend { position: fixed; bottom: 16px; left: 16px; background: #1e293b; padding: 12px 16px; border-radius: 10px; font-size: 12px; border: 1px solid #334155; display: flex; gap: 16px; align-items: center; }\n .legend-dot { width: 12px; height: 12px; border-radius: 3px; display: inline-block; vertical-align: middle; margin-right: 6px; }\n #tooltip { position: fixed; display: none; background: #1e293b; border: 1px solid #475569; border-radius: 8px; padding: 12px 16px; font-size: 12px; max-width: 320px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); z-index: 10; pointer-events: none; }\n #tooltip h3 { font-size: 14px; margin-bottom: 6px; }\n #tooltip .type-badge { display: inline-block; padding: 1px 8px; border-radius: 4px; font-size: 10px; font-weight: 700; text-transform: uppercase; margin-left: 8px; }\n #tooltip .dep-section { margin-top: 8px; color: #94a3b8; }\n #tooltip ul { list-style: none; padding-left: 0; margin-top: 4px; }\n #tooltip li { padding: 1px 0; color: #cbd5e1; }\n #tooltip li::before { content: "92 "; color: #64748b; }\n</style>\n</head>\n<body>\n<div id="info">\n <b class="app-count">', '</b> apps &middot;\n <b class="lib-count">', '</b> libraries &middot;\n <b class="dep-count">', '</b> dependencies\n</div>\n<div id="legend">\n <span><span class="legend-dot" style="background:#fbbf24"></span>Application</span>\n <span><span class="legend-dot" style="background:#38bdf8"></span>Library</span>\n <span style="color:#64748b">&mdash; solid = static &nbsp; - - - = implicit</span>\n</div>\n<div id="tooltip"></div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#64748b"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst tooltip = document.getElementById('tooltip');\nconst W = window.innerWidth, H = window.innerHeight;\n\n// Build adjacency\nconst depMap = {}, rdepMap = {};\ndata.nodes.forEach(n => { depMap[n.name] = []; rdepMap[n.name] = []; });\ndata.edges.forEach(e => { depMap[e.source]?.push(e.target); rdepMap[e.target]?.push(e.source); });\n\n// Escape text for safe DOM insertion\nfunction esc(s) { const d = document.createElement('div'); d.textContent = s; return d.textContent; }\n\n// Force-directed layout\nconst repulsion = 5000 + data.nodes.length * 150;\nconst nodes = data.nodes.map(n => ({\n ...n, x: W/2 + (Math.random()-0.5)*Math.min(W*0.6, 600),\n y: H/2 + (Math.random()-0.5)*Math.min(H*0.6, 400), vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.name, n]));\nconst edges = data.edges.map(e => ({\n source: nodeMap.get(e.source), target: nodeMap.get(e.target), type: e.type\n}));\n\nfor (let iter = 0; iter < 400; iter++) {\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = repulsion / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n edges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 180) * 0.008;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001; n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.75; n.vy *= 0.75;\n n.x = Math.max(80, Math.min(W-80, n.x));\n n.y = Math.max(40, Math.min(H-40, n.y));\n });\n}\n\n// Measure text widths\nconst measure = document.createElementNS('http://www.w3.org/2000/svg','text');\nmeasure.setAttribute('font-size','12'); measure.setAttribute('font-weight','600');\nmeasure.setAttribute('font-family','system-ui');\nsvg.appendChild(measure);\nconst widths = {};\nnodes.forEach(n => { measure.textContent = n.name; widths[n.name] = measure.getComputedTextLength(); });\nsvg.removeChild(measure);\n\n// Render edges\nedges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n const sw = (widths[e.source.name]||80)/2 + 12;\n const tw = (widths[e.target.name]||80)/2 + 12;\n const dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n const d = Math.sqrt(dx*dx+dy*dy)||1;\n const x1 = e.source.x + dx/d*sw, y1 = e.source.y + dy/d*14;\n const x2 = e.target.x - dx/d*tw, y2 = e.target.y - dy/d*14;\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1',x1); line.setAttribute('y1',y1);\n line.setAttribute('x2',x2); line.setAttribute('y2',y2);\n line.setAttribute('class','edge');\n const edgeColors = { implicit: '#475569', devDependency: '#888888', peerDependency: '#CC8800' };\n line.setAttribute('stroke', edgeColors[e.type] || '#64748b');\n line.setAttribute('stroke-width', '1.5');\n if (e.type === 'implicit' || e.type === 'peerDependency') {\n line.setAttribute('stroke-dasharray', '6,4');\n }\n if (e.type === 'devDependency') {\n line.setAttribute('stroke-dasharray', '3,3');\n }\n svg.appendChild(line);\n});\n\n// Render nodes\nnodes.forEach(n => {\n const w = (widths[n.name]||80) + 24;\n const h = 32;\n const isApp = n.type === 'application';\n const fill = isApp ? '#fbbf24' : '#38bdf8';\n const textFill = '#0f172a';\n const stroke = isApp ? '#f59e0b' : '#0284c7';\n\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x - w/2)+','+(n.y - h/2)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width', w); rect.setAttribute('height', h);\n rect.setAttribute('fill', fill); rect.setAttribute('stroke', stroke); rect.setAttribute('stroke-width','1.5');\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x', w/2); text.setAttribute('y', h/2 + 4.5);\n text.setAttribute('text-anchor','middle'); text.setAttribute('fill', textFill);\n text.textContent = n.name;\n g.appendChild(text);\n\n g.addEventListener('mouseenter', (ev) => {\n const deps = depMap[n.name] || [];\n const rdeps = rdepMap[n.name] || [];\n\n // Build tooltip using safe DOM methods\n tooltip.textContent = '';\n\n const heading = document.createElement('h3');\n heading.textContent = n.name;\n const badge = document.createElement('span');\n badge.className = 'type-badge';\n badge.style.background = fill;\n badge.style.color = '#0f172a';\n badge.textContent = n.type;\n heading.appendChild(badge);\n tooltip.appendChild(heading);\n\n if (deps.length) {\n const label = document.createElement('div');\n label.className = 'dep-section';\n label.textContent = 'Depends on:';\n tooltip.appendChild(label);\n const ul = document.createElement('ul');\n deps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });\n tooltip.appendChild(ul);\n }\n if (rdeps.length) {\n const label = document.createElement('div');\n label.className = 'dep-section';\n label.textContent = 'Required by:';\n tooltip.appendChild(label);\n const ul = document.createElement('ul');\n rdeps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });\n tooltip.appendChild(ul);\n }\n if (!deps.length && !rdeps.length) {\n const empty = document.createElement('div');\n empty.style.marginTop = '6px';\n empty.style.color = '#64748b';\n empty.textContent = 'No dependencies';\n tooltip.appendChild(empty);\n }\n\n tooltip.style.display = 'block';\n const rect = tooltip.getBoundingClientRect();\n tooltip.style.left = Math.min(ev.clientX + 12, W - rect.width - 12) + 'px';\n tooltip.style.top = Math.min(ev.clientY + 12, H - rect.height - 12) + 'px';\n });\n g.addEventListener('mouseleave', () => { tooltip.style.display = 'none'; });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"], ['<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="UTF-8">\n<title>Project Dependency Graph</title>\n<style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; overflow: hidden; }\n svg { width: 100vw; height: 100vh; }\n .edge { fill: none; marker-end: url(#arrow); }\n .node rect { rx: 8; ry: 8; cursor: pointer; transition: stroke-width 0.15s; }\n .node text { font-size: 12px; font-weight: 600; pointer-events: none; }\n .node:hover rect { stroke-width: 2.5; stroke: #fff; }\n #info { position: fixed; top: 16px; right: 16px; background: #1e293b; padding: 14px 20px; border-radius: 10px; font-size: 13px; border: 1px solid #334155; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }\n #info b { font-variant-numeric: tabular-nums; }\n .app-count { color: #fbbf24; }\n .lib-count { color: #38bdf8; }\n .dep-count { color: #a78bfa; }\n #legend { position: fixed; bottom: 16px; left: 16px; background: #1e293b; padding: 12px 16px; border-radius: 10px; font-size: 12px; border: 1px solid #334155; display: flex; gap: 16px; align-items: center; }\n .legend-dot { width: 12px; height: 12px; border-radius: 3px; display: inline-block; vertical-align: middle; margin-right: 6px; }\n #tooltip { position: fixed; display: none; background: #1e293b; border: 1px solid #475569; border-radius: 8px; padding: 12px 16px; font-size: 12px; max-width: 320px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); z-index: 10; pointer-events: none; }\n #tooltip h3 { font-size: 14px; margin-bottom: 6px; }\n #tooltip .type-badge { display: inline-block; padding: 1px 8px; border-radius: 4px; font-size: 10px; font-weight: 700; text-transform: uppercase; margin-left: 8px; }\n #tooltip .dep-section { margin-top: 8px; color: #94a3b8; }\n #tooltip ul { list-style: none; padding-left: 0; margin-top: 4px; }\n #tooltip li { padding: 1px 0; color: #cbd5e1; }\n #tooltip li::before { content: "\\2192 "; color: #64748b; }\n</style>\n</head>\n<body>\n<div id="info">\n <b class="app-count">', '</b> apps &middot;\n <b class="lib-count">', '</b> libraries &middot;\n <b class="dep-count">', '</b> dependencies\n</div>\n<div id="legend">\n <span><span class="legend-dot" style="background:#fbbf24"></span>Application</span>\n <span><span class="legend-dot" style="background:#38bdf8"></span>Library</span>\n <span style="color:#64748b">&mdash; solid = static &nbsp; - - - = implicit</span>\n</div>\n<div id="tooltip"></div>\n<svg id="graph">\n <defs>\n <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">\n <path d="M 0 0 L 10 5 L 0 10 z" fill="#64748b"/>\n </marker>\n </defs>\n</svg>\n<script>\nconst data = ', ";\nconst svg = document.getElementById('graph');\nconst tooltip = document.getElementById('tooltip');\nconst W = window.innerWidth, H = window.innerHeight;\n\n// Build adjacency\nconst depMap = {}, rdepMap = {};\ndata.nodes.forEach(n => { depMap[n.name] = []; rdepMap[n.name] = []; });\ndata.edges.forEach(e => { depMap[e.source]?.push(e.target); rdepMap[e.target]?.push(e.source); });\n\n// Escape text for safe DOM insertion\nfunction esc(s) { const d = document.createElement('div'); d.textContent = s; return d.textContent; }\n\n// Force-directed layout\nconst repulsion = 5000 + data.nodes.length * 150;\nconst nodes = data.nodes.map(n => ({\n ...n, x: W/2 + (Math.random()-0.5)*Math.min(W*0.6, 600),\n y: H/2 + (Math.random()-0.5)*Math.min(H*0.6, 400), vx: 0, vy: 0\n}));\nconst nodeMap = new Map(nodes.map(n => [n.name, n]));\nconst edges = data.edges.map(e => ({\n source: nodeMap.get(e.source), target: nodeMap.get(e.target), type: e.type\n}));\n\nfor (let iter = 0; iter < 400; iter++) {\n for (let i = 0; i < nodes.length; i++) {\n for (let j = i+1; j < nodes.length; j++) {\n let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = repulsion / (d * d);\n nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;\n nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;\n }\n }\n edges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n let d = Math.sqrt(dx*dx + dy*dy) || 1;\n let f = (d - 180) * 0.008;\n e.source.vx += dx/d * f; e.source.vy += dy/d * f;\n e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;\n });\n nodes.forEach(n => {\n n.vx += (W/2 - n.x) * 0.001; n.vy += (H/2 - n.y) * 0.001;\n n.x += n.vx * 0.3; n.y += n.vy * 0.3;\n n.vx *= 0.75; n.vy *= 0.75;\n n.x = Math.max(80, Math.min(W-80, n.x));\n n.y = Math.max(40, Math.min(H-40, n.y));\n });\n}\n\n// Measure text widths\nconst measure = document.createElementNS('http://www.w3.org/2000/svg','text');\nmeasure.setAttribute('font-size','12'); measure.setAttribute('font-weight','600');\nmeasure.setAttribute('font-family','system-ui');\nsvg.appendChild(measure);\nconst widths = {};\nnodes.forEach(n => { measure.textContent = n.name; widths[n.name] = measure.getComputedTextLength(); });\nsvg.removeChild(measure);\n\n// Render edges\nedges.forEach(e => {\n if (!e.source || !e.target) {\n return;\n }\n const sw = (widths[e.source.name]||80)/2 + 12;\n const tw = (widths[e.target.name]||80)/2 + 12;\n const dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;\n const d = Math.sqrt(dx*dx+dy*dy)||1;\n const x1 = e.source.x + dx/d*sw, y1 = e.source.y + dy/d*14;\n const x2 = e.target.x - dx/d*tw, y2 = e.target.y - dy/d*14;\n const line = document.createElementNS('http://www.w3.org/2000/svg','line');\n line.setAttribute('x1',x1); line.setAttribute('y1',y1);\n line.setAttribute('x2',x2); line.setAttribute('y2',y2);\n line.setAttribute('class','edge');\n const edgeColors = { implicit: '#475569', devDependency: '#888888', peerDependency: '#CC8800' };\n line.setAttribute('stroke', edgeColors[e.type] || '#64748b');\n line.setAttribute('stroke-width', '1.5');\n if (e.type === 'implicit' || e.type === 'peerDependency') {\n line.setAttribute('stroke-dasharray', '6,4');\n }\n if (e.type === 'devDependency') {\n line.setAttribute('stroke-dasharray', '3,3');\n }\n svg.appendChild(line);\n});\n\n// Render nodes\nnodes.forEach(n => {\n const w = (widths[n.name]||80) + 24;\n const h = 32;\n const isApp = n.type === 'application';\n const fill = isApp ? '#fbbf24' : '#38bdf8';\n const textFill = '#0f172a';\n const stroke = isApp ? '#f59e0b' : '#0284c7';\n\n const g = document.createElementNS('http://www.w3.org/2000/svg','g');\n g.setAttribute('class','node');\n g.setAttribute('transform','translate('+(n.x - w/2)+','+(n.y - h/2)+')');\n const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');\n rect.setAttribute('width', w); rect.setAttribute('height', h);\n rect.setAttribute('fill', fill); rect.setAttribute('stroke', stroke); rect.setAttribute('stroke-width','1.5');\n g.appendChild(rect);\n const text = document.createElementNS('http://www.w3.org/2000/svg','text');\n text.setAttribute('x', w/2); text.setAttribute('y', h/2 + 4.5);\n text.setAttribute('text-anchor','middle'); text.setAttribute('fill', textFill);\n text.textContent = n.name;\n g.appendChild(text);\n\n g.addEventListener('mouseenter', (ev) => {\n const deps = depMap[n.name] || [];\n const rdeps = rdepMap[n.name] || [];\n\n // Build tooltip using safe DOM methods\n tooltip.textContent = '';\n\n const heading = document.createElement('h3');\n heading.textContent = n.name;\n const badge = document.createElement('span');\n badge.className = 'type-badge';\n badge.style.background = fill;\n badge.style.color = '#0f172a';\n badge.textContent = n.type;\n heading.appendChild(badge);\n tooltip.appendChild(heading);\n\n if (deps.length) {\n const label = document.createElement('div');\n label.className = 'dep-section';\n label.textContent = 'Depends on:';\n tooltip.appendChild(label);\n const ul = document.createElement('ul');\n deps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });\n tooltip.appendChild(ul);\n }\n if (rdeps.length) {\n const label = document.createElement('div');\n label.className = 'dep-section';\n label.textContent = 'Required by:';\n tooltip.appendChild(label);\n const ul = document.createElement('ul');\n rdeps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });\n tooltip.appendChild(ul);\n }\n if (!deps.length && !rdeps.length) {\n const empty = document.createElement('div');\n empty.style.marginTop = '6px';\n empty.style.color = '#64748b';\n empty.textContent = 'No dependencies';\n tooltip.appendChild(empty);\n }\n\n tooltip.style.display = 'block';\n const rect = tooltip.getBoundingClientRect();\n tooltip.style.left = Math.min(ev.clientX + 12, W - rect.width - 12) + 'px';\n tooltip.style.top = Math.min(ev.clientY + 12, H - rect.height - 12) + 'px';\n });\n g.addEventListener('mouseleave', () => { tooltip.style.display = 'none'; });\n svg.appendChild(g);\n});\n<\/script>\n</body>\n</html>"])), graphData.apps, graphData.libs, graphData.edges.length, JSON.stringify(graphData).replaceAll("</", String.raw`<\/`));
927
- };
928
- const execute = async ({ logger, options, visConfig, workspaceRoot: wsRoot }) => {
929
- if (!wsRoot) {
930
- throw new Error("Could not determine workspace root. Run this command inside a monorepo.");
931
- }
932
- const workspaceRoot = wsRoot;
933
- const { packageJsons, workspace } = discoverWorkspace(workspaceRoot, visConfig);
934
- const projectGraph = buildProjectGraph(workspaceRoot, workspace, packageJsons);
935
- const isTTY = Boolean(process.stdout.isTTY) && !isInCi;
936
- const format = options.format ?? (isTTY ? "tui" : "ascii");
937
- const outputFile = options.output;
938
- const maxDepth = options.depth ?? Infinity;
939
- let output;
940
- switch (format) {
941
- case "dot": {
942
- output = projectGraphToDot(projectGraph);
943
- break;
944
- }
945
- case "html": {
946
- output = projectGraphToHtml(projectGraph);
947
- break;
948
- }
949
- case "json": {
950
- output = JSON.stringify(projectGraphToJson(projectGraph), void 0, 2);
951
- break;
952
- }
953
- case "tui": {
954
- if (!isTTY) {
955
- output = projectGraphToAscii(projectGraph, maxDepth);
956
- break;
957
- }
958
- const autoExitSeconds = visConfig?.tui?.autoExit === true ? 3 : typeof visConfig?.tui?.autoExit === "number" ? visConfig.tui.autoExit : 0;
959
- if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
960
- process.stdin.setRawMode(true);
961
- process.stdin.ref();
962
- process.stdin.resume();
963
- }
964
- const keepAlive = setInterval(() => {
965
- }, 1e3);
966
- const store = new GraphStore(projectGraph);
967
- const instance = render(React.createElement(VisGraphApp, { autoExitSeconds, store }), {
968
- alternateScreen: true,
969
- exitOnCtrlC: false,
970
- interactive: true,
971
- patchConsole: true
972
- });
973
- await instance.waitUntilExit();
974
- clearInterval(keepAlive);
975
- return;
976
- }
977
- default: {
978
- output = projectGraphToAscii(projectGraph, maxDepth);
979
- }
980
- }
981
- if (outputFile) {
982
- writeFileSync(outputFile, output, "utf8");
983
- logger.info(`Graph written to ${outputFile}`);
984
- } else {
985
- logger.info(output);
986
- }
987
- };
988
-
989
- export { execute as default };
1
+ var re=Object.defineProperty;var b=(e,t)=>re(e,"name",{value:t,configurable:!0});import{createRequire as ne}from"node:module";import{dim as v,cyan as se,yellow as E,red as oe,green as W}from"@visulima/colorize";import{isAccessibleSync as D,readJsonSync as ae,writeFileSync as ce,readFileSync as le}from"@visulima/fs";import{isAbsolute as C,relative as H,resolve as j,join as B}from"@visulima/path";import{redact as de}from"@visulima/redact";import{fingerprint as k,listRules as K,listRequiredValidators as ue,scan as Q,scanFiles as fe,inspectRuleset as pe}from"@visulima/secret-scanner";import{p as m}from"./bin.js";import{InteractiveManager as ge,InteractiveStreamHook as V}from"@visulima/interactive-manager";import{Spinner as me}from"@visulima/spinner";const ie=ne(import.meta.url),F=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,G=b(e=>{if(typeof F<"u"&&F.versions&&F.versions.node){const[t,i]=F.versions.node.split(".").map(Number);if(t>22||t===22&&i>=3||t===20&&i>=16)return F.getBuiltinModule(e)}return ie(e)},"__cjs_getBuiltinModule"),{pathToFileURL:X}=G("node:url"),{execFileSync:he}=G("node:child_process");var we=Object.defineProperty,ve=b((e,t)=>we(e,"name",{value:t,configurable:!0}),"r$1");const Y=ve((e={})=>{const t=new ge(new V(process.stdout),new V(process.stderr));return new me(e,t)},"createSpinner");var $e=Object.defineProperty,I=b((e,t)=>$e(e,"name",{value:t,configurable:!0}),"s");const ye=I((e,t)=>{if(!C(e))return e;const i=H(t,e);return i===""||i.startsWith("..")?e:i},"toRelative"),A=I((e,t)=>{const i=ye(e.file,t);return i===e.file?e:{...e,file:i}},"toRelativeFinding"),Z=I(e=>{if(!D(e))return[];try{const t=ae(e);return Array.isArray(t)?t:[]}catch{return[]}},"readBaseline"),Se=I((e,t,i)=>{const n=Z(t).map(o=>A(o,i)),r=new Set(n.map(o=>k(o))),a=e.map(o=>A(o,i)),l=new Set(a.map(o=>k(o)));return{fresh:a.filter(o=>!r.has(k(o))),resolved:n.filter(o=>!l.has(k(o))),surviving:a.filter(o=>r.has(k(o)))}},"diffBaseline"),ee=I((e,t,i,n={})=>{const r=e.map(l=>A(l,i));let a;if(n.replace)a=r;else{const l=Z(t).map(s=>A(s,i)),o=new Set;a=[];for(const s of[...l,...r]){const c=k(s);o.has(c)||(o.add(c),a.push(s))}}return ce(t,`${JSON.stringify(a,null,4)}
2
+ `),a.length},"writeBaseline");var be=Object.defineProperty,h=b((e,t)=>be(e,"name",{value:t,configurable:!0}),"a$1");const U=1,xe=h(e=>{const t=new Map;for(const i of e){const n=t.get(i.file);n?n.push(i):t.set(i.file,[i])}return t},"groupByFile"),ke=h(e=>{try{return le(e).split(/\r?\n/)}catch{return}},"loadLines"),je=h((e,t,i)=>{const n=Math.max(1,t);return`${e.slice(0,n-1).replaceAll(/[^\t]/g," ")}${"^".repeat(Math.max(1,i))}`},"caretFor"),Ce=h((e,t,i,n={})=>{if(e.length===0)return i?v("No secrets detected."):"No secrets detected.";const r=i?{cyan:se,dim:v,green:W,red:oe,yellow:E}:{cyan:h(o=>o,"cyan"),dim:h(o=>o,"dim"),green:h(o=>o,"green"),red:h(o=>o,"red"),yellow:h(o=>o,"yellow")},a=[],l=xe(e);for(const[o,s]of l){const c=H(t,o)||o;a.push(r.cyan(c));const p=n.redact?void 0:ke(o);for(const d of s){const x=[d.source,d.confidence].filter(Boolean).join(", "),S=x?` ${r.dim(`(${x})`)}`:"",w=d.alternateMatches&&d.alternateMatches.length>0?` ${r.dim(`also: ${d.alternateMatches.join(", ")}`)}`:"";let u="";switch(d.validation){case"error":{u=` ${r.yellow("! error")}`;break}case"rejected":{u=` ${r.red("✗ rejected")}`;break}case"skipped":{u=` ${r.dim("— unverifiable")}`;break}case"verified":{u=` ${r.green("✓ verified")}`;break}}if(a.push(` ${r.red("✖")} ${r.yellow(`[${d.ruleId}]`)}${S}${u} ${r.dim(`line ${String(d.startLine)}:${String(d.startColumn)}`)}${w}`),p){const R=Math.max(0,d.startLine-1-U),$=Math.min(p.length,d.startLine+U);for(let y=R;y<$;y+=1){const q=String(y+1).padStart(4," "),L=y+1===d.startLine,g=L?r.red("▶"):" ";if(a.push(` ${g} ${r.dim(q)} │ ${p[y]??""}`),L){const N=Math.max(1,(d.endColumn??d.startColumn+1)-d.startColumn),te=je(p[y]??"",d.startColumn,N);a.push(` ${r.dim(" │ ")}${r.red(te)}`)}}}a.push("")}}return a.join(`
3
+ `).trimEnd()},"formatText"),Re=h((e,t)=>{if(!C(e))return e.replaceAll("\\","/");try{return X(e).toString()}catch{return`file://${j(t,e).replaceAll("\\","/")}`}},"toSarifUri"),Fe=h((e,t=100)=>e.length<=t?e:`${e.slice(0,t-1).trimEnd()}…`,"shortText"),Me=h((e,t,i=process.cwd(),n=[])=>{const r=new Map(n.map(s=>[s.id,s])),a=new Set;for(const s of e)a.add(s.ruleId);const l=[...new Set([...r.keys(),...a])].sort((s,c)=>s.localeCompare(c)),o=new Map(l.map((s,c)=>[s,c]));return JSON.stringify({$schema:"https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json",runs:[{originalUriBaseIds:{SRCROOT:{uri:X(i).toString()}},results:e.map(s=>{const c={};return s.source&&(c.source=s.source),s.confidence&&(c.confidence=s.confidence),s.alternateMatches&&s.alternateMatches.length>0&&(c.alternateRules=s.alternateMatches),s.validation&&(c.validation=s.validation),{level:"error",locations:[{physicalLocation:{artifactLocation:{uri:Re(s.file,i),uriBaseId:C(s.file)?void 0:"SRCROOT"},region:{endColumn:s.endColumn,endLine:s.endLine,snippet:{text:s.match},startColumn:s.startColumn,startLine:s.startLine}}}],message:{text:s.description||s.ruleId},properties:Object.keys(c).length>0?c:void 0,ruleId:s.ruleId,ruleIndex:o.get(s.ruleId)??-1}}),tool:{driver:{informationUri:"https://visulima.com/packages/secret-scanner",name:"visulima-secret-scanner",rules:l.map(s=>{const c=r.get(s),p=c?.description??`Detected by rule \`${s}\``,d={};return c?.tags&&c.tags.length>0&&(d.tags=c.tags),c?.source&&(d.source=c.source),c?.confidence&&(d.confidence=c.confidence),{defaultConfiguration:{level:"error"},fullDescription:{text:p},helpUri:"https://visulima.com/packages/secret-scanner",id:s,name:s,properties:Object.keys(d).length>0?d:void 0,shortDescription:{text:Fe(p)}}}),version:t}}}],version:"2.1.0"},void 0,2)},"formatSarif");var Be=Object.defineProperty,T=b((e,t)=>Be(e,"name",{value:t,configurable:!0}),"n");const O=T((e,t)=>{try{return he("git",t,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch{return""}},"runGit"),_=T(e=>e.split(/\r?\n/).map(t=>t.trim()).filter(Boolean),"splitFiles"),Ae=T(e=>_(O(e,["diff","--cached","--name-only","--diff-filter=ACMR"])).map(t=>C(t)?t:B(e,t)),"stagedFiles"),z=T((e,t)=>{const i=O(e,["diff","--name-only","--diff-filter=ACMR",`${t}...HEAD`]),n=_(i);if(n.length===0){const r=O(e,["diff","--name-only","--diff-filter=ACMR",t]);return _(r).map(a=>C(a)?a:B(e,a))}return n.map(r=>C(r)?r:B(e,r))},"filesSince"),P=T(e=>O(e,["rev-parse","--show-toplevel"]).length>0,"hasGit");var Ie=Object.defineProperty,f=b((e,t)=>Ie(e,"name",{value:t,configurable:!0}),"c");const M=".secrets-baseline.json",J=f(e=>e?Array.isArray(e)?e:[e]:[],"toArray"),Te=f(e=>{const t=new Set(["json","sarif","text"]);if(e&&!t.has(e))throw new Error(`--format must be one of: ${[...t].join(", ")} (got "${e}")`);return e??"text"},"validateFormat"),qe=f(e=>{if(e===void 0)return;const t=new Set(["high","low","medium"]);if(!t.has(e))throw new Error(`--min-confidence must be one of: ${[...t].join(", ")} (got "${e}")`);return e},"validateConfidence"),Le=f(async(e,t)=>{const i=await K(e);process.stdout.write(`${String(i.length)} rules loaded
4
+
5
+ `);for(const n of i){const r=t?E(n.id):n.id,a=n.tags.length>0?` ${t?v(`[${n.tags.join(", ")}]`):`[${n.tags.join(", ")}]`}`:"";if(process.stdout.write(`${r}${a}
6
+ ${n.description}
7
+ `),n.keywords.length>0){const l=`keywords: ${n.keywords.slice(0,6).join(", ")}${n.keywords.length>6?", ...":""}`;process.stdout.write(` ${t?v(l):l}
8
+ `)}process.stdout.write(`
9
+ `)}},"printListRules"),Ne=f(async(e,t)=>{const i=await ue(e);if(i.length===0){process.stdout.write(t?`${v("No non-HTTP validators required by the current ruleset.")}
10
+ `:`No non-HTTP validators required by the current ruleset.
11
+ `);return}process.stdout.write(`${String(i.length)} non-HTTP validator type(s) referenced by the current ruleset:
12
+
13
+ `);for(const n of i){const r=t?E(n.displayName):n.displayName,a=`(${n.type}, ${String(n.ruleCount)} rule${n.ruleCount===1?"":"s"})`;process.stdout.write(`${r} ${t?v(a):a}
14
+ `),process.stdout.write(` ${n.summary}
15
+ `);const l=n.packageName?`install: npm add ${n.packageName}`:"no driver — bespoke implementation required";process.stdout.write(` ${t?v(l):l}
16
+
17
+ `)}},"printListValidators"),Oe=f(async(e,t,i)=>{const n=B(e,M);if(!i&&D(n)){m.warn(`Detected existing ${M} — refusing to overwrite. Delete it first to re-init.`),process.exitCode=1;return}m.info(i?"[dry-run] Previewing init — no files will be written.":"Scanning workspace to seed baseline…");const r=Y();r.start("scanning");let a;try{a=await Q([e],t),r.succeed()}catch(o){throw r.failed(),o}if(i){m.info(`[dry-run] Would create ${M} with ${String(a.length)} finding(s).`);return}const l=ee(a,n,e,{replace:!0});m.success(`Wrote ${M} (${String(l)} findings).`),m.notice("Commit it. Use `vis secrets --baseline .secrets-baseline.json` in CI. Add path patterns to .gitignore to exclude directories from scanning.")},"runInit"),Ee=f((e,t,i)=>{const n=t??{},r=f(u=>u?j(i,u):void 0,"resolvePath"),a=f((u,R)=>{const $=J(u);return $.length>0?$:R},"pickList"),l=a(e.enableRule,n.rules?.enable),o=a(e.excludeRule,n.rules?.exclude),s=a(e.includeRule,n.rules?.include),c=a(e.exclude,n.walk?.excludePatterns),p=J(e.excludeFrom).map(u=>j(i,u)),d=p.length>0?p:n.walk?.excludeFromFiles?.map(u=>j(i,u)),x=r(e.baseline)??r(n.baseline),S=r(e.config)??r(n.config?.path),w=qe(e.minConfidence??n.config?.minConfidence);return{baseline:x,concurrency:e.concurrency,config:{extendBundled:e.noExtendBundled?!1:n.config?.extendBundled,inline:n.config?.inline,minConfidence:w,onlyVerified:e.onlyVerified??n.config?.onlyVerified??!1,path:S,validate:e.validate??n.config?.validate??!1},redact:e.redact??n.redact,rules:{enable:l,exclude:o,include:s},verbose:e.verbose??!1,walk:{excludeFromFiles:d,excludePatterns:c,gitignore:e.noGitignore?!1:n.walk?.gitignore??!0,includeHidden:e.includeHidden??n.walk?.includeHidden,maxFileSize:e.maxSize??n.walk?.maxFileSize}}},"resolveScanOptions"),Pe=f(e=>{process.stderr.write(`${v("baseline diff: ")}${W(`+${String(e.fresh.length)} new`)} · ${E(`${String(e.surviving.length)} unchanged`)} · ${v(`-${String(e.resolved.length)} resolved`)}
18
+ `)},"printDiff"),_e=f(async(e,t,i)=>{if(e.staged){if(!P(i))throw new Error("--staged requires a git working tree, and none was detected.");return{files:Ae(i)}}if(e.since){if(!P(i))throw new Error("--since requires a git working tree, and none was detected.");return{files:z(i,e.since)}}if(e.affected){if(!P(i))return m.warn("--affected requires git; falling back to full scan"),{paths:t&&t.length>0?t.map(r=>j(i,r)):[i]};const n=process.env.VIS_BASE??"HEAD~1";return{files:z(i,n)}}return{paths:t&&t.length>0?t.map(n=>j(i,n)):[i]}},"chooseScanPaths"),De=f((e,t,i,n,r,a,l)=>{switch(t){case"json":{const o=e.map(s=>A(s,i));process.stdout.write(`${JSON.stringify(o,null,2)}
19
+ `);break}case"sarif":{process.stdout.write(`${Me(e,r,i,a)}
20
+ `);break}default:process.stdout.write(`${Ce(e,i,n,{redact:l})}
21
+ `)}},"emitFindings"),Ye=f(async({argument:e,options:t,visConfig:i,workspaceRoot:n})=>{const r=t,a=e,l=n??process.cwd(),o=!r.quiet&&process.stdout.isTTY,s=i?.secrets,c=Ee(r,s,l),p="0.0.0-alpha";if(r.listRules){await Le(c,o);return}if(r.listValidators){await Ne(c,o);return}if(r.init){await Oe(l,c,r.dryRun??!1);return}const d=await _e(r,a??[],l);if(d.files?.length===0){r.quiet||m.success("No files to scan.");return}const x=!r.quiet&&!["json","sarif"].includes(r.format??"text"),S=Y({verbose:x});S.start("scanning for secrets");let w;try{w=d.files===void 0?await Q(d.paths??[l],c):await fe(d.files,c),S.succeed()}catch(g){throw S.failed(),new Error(`secret scan failed: ${g instanceof Error?g.message:String(g)}`,{cause:g})}if(r.verbose){const g=await pe(c);if(g.length>0){m.warn(`${String(g.length)} rule(s) skipped due to invalid regex. First few:`);for(const N of g.slice(0,5))process.stderr.write(` - ${N.ruleId}: ${N.reason}
22
+ `)}}const u=c.baseline??B(l,M),R=!r.quiet&&D(u);if(r.updateBaseline){const g=ee(w,u,l,{replace:r.replaceBaseline});m.success(`Baseline updated: ${H(l,u)||u} now contains ${String(g)} entries.`);return}const $=Te(r.format),y=$==="sarif"?await K(c).catch(()=>[]):[],q=c.redact===!0,L=q?de(w,["match","secret"]):w;if(De(L,$,l,o,p,y,q),$==="text"&&R&&Pe(Se(w,u,l)),w.length>0){r.quiet||(m.warn(`${String(w.length)} potential secret(s) found`),m.notice("Suppress individual lines with `gitleaks:allow` / `secret-scanner:allow`, or run `vis secrets --update-baseline`.")),process.exitCode=1;return}r.quiet||m.success("No secrets detected.")},"execute");export{Ye as default};