@opensip-cli/dashboard 0.1.11 → 0.1.12

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 (148) hide show
  1. package/README.md +2 -2
  2. package/dist/client-bundle.generated.d.ts.map +1 -1
  3. package/dist/client-bundle.generated.js +1 -1
  4. package/dist/client-bundle.generated.js.map +1 -1
  5. package/dist/generator.d.ts.map +1 -1
  6. package/dist/generator.js +4 -3
  7. package/dist/generator.js.map +1 -1
  8. package/dist/report-cup-icon.d.ts +17 -0
  9. package/dist/report-cup-icon.d.ts.map +1 -0
  10. package/dist/report-cup-icon.js +34 -0
  11. package/dist/report-cup-icon.js.map +1 -0
  12. package/package.json +3 -3
  13. package/dist/__tests__/catalog-provenance.test.d.ts +0 -11
  14. package/dist/__tests__/catalog-provenance.test.d.ts.map +0 -1
  15. package/dist/__tests__/catalog-provenance.test.js +0 -112
  16. package/dist/__tests__/catalog-provenance.test.js.map +0 -1
  17. package/dist/__tests__/coupling-attribution.test.d.ts +0 -9
  18. package/dist/__tests__/coupling-attribution.test.d.ts.map +0 -1
  19. package/dist/__tests__/coupling-attribution.test.js +0 -95
  20. package/dist/__tests__/coupling-attribution.test.js.map +0 -1
  21. package/dist/__tests__/dashboard-bundle-weight.test.d.ts +0 -23
  22. package/dist/__tests__/dashboard-bundle-weight.test.d.ts.map +0 -1
  23. package/dist/__tests__/dashboard-bundle-weight.test.js +0 -80
  24. package/dist/__tests__/dashboard-bundle-weight.test.js.map +0 -1
  25. package/dist/__tests__/dashboard-cell-containment.test.d.ts +0 -2
  26. package/dist/__tests__/dashboard-cell-containment.test.d.ts.map +0 -1
  27. package/dist/__tests__/dashboard-cell-containment.test.js +0 -52
  28. package/dist/__tests__/dashboard-cell-containment.test.js.map +0 -1
  29. package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts +0 -10
  30. package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts.map +0 -1
  31. package/dist/__tests__/dashboard-cytoscape-vendor.test.js +0 -40
  32. package/dist/__tests__/dashboard-cytoscape-vendor.test.js.map +0 -1
  33. package/dist/__tests__/dashboard-editor-link.test.d.ts +0 -14
  34. package/dist/__tests__/dashboard-editor-link.test.d.ts.map +0 -1
  35. package/dist/__tests__/dashboard-editor-link.test.js +0 -87
  36. package/dist/__tests__/dashboard-editor-link.test.js.map +0 -1
  37. package/dist/__tests__/dashboard-el.test.d.ts +0 -18
  38. package/dist/__tests__/dashboard-el.test.d.ts.map +0 -1
  39. package/dist/__tests__/dashboard-el.test.js +0 -90
  40. package/dist/__tests__/dashboard-el.test.js.map +0 -1
  41. package/dist/__tests__/dashboard-filters.test.d.ts +0 -9
  42. package/dist/__tests__/dashboard-filters.test.d.ts.map +0 -1
  43. package/dist/__tests__/dashboard-filters.test.js +0 -87
  44. package/dist/__tests__/dashboard-filters.test.js.map +0 -1
  45. package/dist/__tests__/dashboard-function-card-singleton.test.d.ts +0 -9
  46. package/dist/__tests__/dashboard-function-card-singleton.test.d.ts.map +0 -1
  47. package/dist/__tests__/dashboard-function-card-singleton.test.js +0 -75
  48. package/dist/__tests__/dashboard-function-card-singleton.test.js.map +0 -1
  49. package/dist/__tests__/dashboard-function-card.test.d.ts +0 -10
  50. package/dist/__tests__/dashboard-function-card.test.d.ts.map +0 -1
  51. package/dist/__tests__/dashboard-function-card.test.js +0 -439
  52. package/dist/__tests__/dashboard-function-card.test.js.map +0 -1
  53. package/dist/__tests__/dashboard-function-row.test.d.ts +0 -11
  54. package/dist/__tests__/dashboard-function-row.test.d.ts.map +0 -1
  55. package/dist/__tests__/dashboard-function-row.test.js +0 -102
  56. package/dist/__tests__/dashboard-function-row.test.js.map +0 -1
  57. package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts +0 -9
  58. package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts.map +0 -1
  59. package/dist/__tests__/dashboard-generator-graph-catalog.test.js +0 -77
  60. package/dist/__tests__/dashboard-generator-graph-catalog.test.js.map +0 -1
  61. package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts +0 -13
  62. package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts.map +0 -1
  63. package/dist/__tests__/dashboard-graph-offline.integration.test.js +0 -57
  64. package/dist/__tests__/dashboard-graph-offline.integration.test.js.map +0 -1
  65. package/dist/__tests__/dashboard-graph-scc.test.d.ts +0 -17
  66. package/dist/__tests__/dashboard-graph-scc.test.d.ts.map +0 -1
  67. package/dist/__tests__/dashboard-graph-scc.test.js +0 -89
  68. package/dist/__tests__/dashboard-graph-scc.test.js.map +0 -1
  69. package/dist/__tests__/dashboard-graph-view-model.test.d.ts +0 -12
  70. package/dist/__tests__/dashboard-graph-view-model.test.d.ts.map +0 -1
  71. package/dist/__tests__/dashboard-graph-view-model.test.js +0 -331
  72. package/dist/__tests__/dashboard-graph-view-model.test.js.map +0 -1
  73. package/dist/__tests__/dashboard-help-drawer.test.d.ts +0 -9
  74. package/dist/__tests__/dashboard-help-drawer.test.d.ts.map +0 -1
  75. package/dist/__tests__/dashboard-help-drawer.test.js +0 -95
  76. package/dist/__tests__/dashboard-help-drawer.test.js.map +0 -1
  77. package/dist/__tests__/dashboard-html.test.d.ts +0 -2
  78. package/dist/__tests__/dashboard-html.test.d.ts.map +0 -1
  79. package/dist/__tests__/dashboard-html.test.js +0 -169
  80. package/dist/__tests__/dashboard-html.test.js.map +0 -1
  81. package/dist/__tests__/dashboard-indexes.test.d.ts +0 -11
  82. package/dist/__tests__/dashboard-indexes.test.d.ts.map +0 -1
  83. package/dist/__tests__/dashboard-indexes.test.js +0 -214
  84. package/dist/__tests__/dashboard-indexes.test.js.map +0 -1
  85. package/dist/__tests__/dashboard-pagination.test.d.ts +0 -15
  86. package/dist/__tests__/dashboard-pagination.test.d.ts.map +0 -1
  87. package/dist/__tests__/dashboard-pagination.test.js +0 -140
  88. package/dist/__tests__/dashboard-pagination.test.js.map +0 -1
  89. package/dist/__tests__/dashboard-path-utils.test.d.ts +0 -13
  90. package/dist/__tests__/dashboard-path-utils.test.d.ts.map +0 -1
  91. package/dist/__tests__/dashboard-path-utils.test.js +0 -62
  92. package/dist/__tests__/dashboard-path-utils.test.js.map +0 -1
  93. package/dist/__tests__/dashboard-search.test.d.ts +0 -10
  94. package/dist/__tests__/dashboard-search.test.d.ts.map +0 -1
  95. package/dist/__tests__/dashboard-search.test.js +0 -52
  96. package/dist/__tests__/dashboard-search.test.js.map +0 -1
  97. package/dist/__tests__/dashboard-sessions.test.d.ts +0 -14
  98. package/dist/__tests__/dashboard-sessions.test.d.ts.map +0 -1
  99. package/dist/__tests__/dashboard-sessions.test.js +0 -400
  100. package/dist/__tests__/dashboard-sessions.test.js.map +0 -1
  101. package/dist/__tests__/dashboard-trace.test.d.ts +0 -16
  102. package/dist/__tests__/dashboard-trace.test.d.ts.map +0 -1
  103. package/dist/__tests__/dashboard-trace.test.js +0 -170
  104. package/dist/__tests__/dashboard-trace.test.js.map +0 -1
  105. package/dist/__tests__/dashboard-validation.integration.test.d.ts +0 -14
  106. package/dist/__tests__/dashboard-validation.integration.test.d.ts.map +0 -1
  107. package/dist/__tests__/dashboard-validation.integration.test.js +0 -221
  108. package/dist/__tests__/dashboard-validation.integration.test.js.map +0 -1
  109. package/dist/__tests__/dashboard-view-conformance.test.d.ts +0 -12
  110. package/dist/__tests__/dashboard-view-conformance.test.d.ts.map +0 -1
  111. package/dist/__tests__/dashboard-view-conformance.test.js +0 -52
  112. package/dist/__tests__/dashboard-view-conformance.test.js.map +0 -1
  113. package/dist/__tests__/dashboard-view-coupling.test.d.ts +0 -7
  114. package/dist/__tests__/dashboard-view-coupling.test.d.ts.map +0 -1
  115. package/dist/__tests__/dashboard-view-coupling.test.js +0 -360
  116. package/dist/__tests__/dashboard-view-coupling.test.js.map +0 -1
  117. package/dist/__tests__/dashboard-view-distribution.test.d.ts +0 -10
  118. package/dist/__tests__/dashboard-view-distribution.test.d.ts.map +0 -1
  119. package/dist/__tests__/dashboard-view-distribution.test.js +0 -249
  120. package/dist/__tests__/dashboard-view-distribution.test.js.map +0 -1
  121. package/dist/__tests__/dashboard-view-graph.test.d.ts +0 -13
  122. package/dist/__tests__/dashboard-view-graph.test.d.ts.map +0 -1
  123. package/dist/__tests__/dashboard-view-graph.test.js +0 -299
  124. package/dist/__tests__/dashboard-view-graph.test.js.map +0 -1
  125. package/dist/__tests__/dashboard-view-template.test.d.ts +0 -20
  126. package/dist/__tests__/dashboard-view-template.test.d.ts.map +0 -1
  127. package/dist/__tests__/dashboard-view-template.test.js +0 -206
  128. package/dist/__tests__/dashboard-view-template.test.js.map +0 -1
  129. package/dist/__tests__/dashboard.test.d.ts +0 -2
  130. package/dist/__tests__/dashboard.test.d.ts.map +0 -1
  131. package/dist/__tests__/dashboard.test.js +0 -129
  132. package/dist/__tests__/dashboard.test.js.map +0 -1
  133. package/dist/__tests__/graph-tab.test.d.ts +0 -10
  134. package/dist/__tests__/graph-tab.test.d.ts.map +0 -1
  135. package/dist/__tests__/graph-tab.test.js +0 -78
  136. package/dist/__tests__/graph-tab.test.js.map +0 -1
  137. package/dist/__tests__/yagni-tab.test.d.ts +0 -13
  138. package/dist/__tests__/yagni-tab.test.d.ts.map +0 -1
  139. package/dist/__tests__/yagni-tab.test.js +0 -85
  140. package/dist/__tests__/yagni-tab.test.js.map +0 -1
  141. package/dist/client/el.d.ts +0 -17
  142. package/dist/client/el.d.ts.map +0 -1
  143. package/dist/client/el.js +0 -36
  144. package/dist/client/el.js.map +0 -1
  145. package/dist/code-paths/__tests__/views-registry.test.d.ts +0 -18
  146. package/dist/code-paths/__tests__/views-registry.test.d.ts.map +0 -1
  147. package/dist/code-paths/__tests__/views-registry.test.js +0 -41
  148. package/dist/code-paths/__tests__/views-registry.test.js.map +0 -1
@@ -1,77 +0,0 @@
1
- /**
2
- * Generator-level wiring tests for the v0.3 graph-catalog inline blob
3
- * and the editor-protocol JS constant.
4
- *
5
- * Covers Phase P0 (catalog block emission, null vs non-null) and the
6
- * Phase P9 hook (editor protocol constant).
7
- */
8
- import { describe, expect, it } from 'vitest';
9
- import { generateDashboardHtml } from '../generator.js';
10
- const minimalCatalog = {
11
- version: '2.0',
12
- tool: 'graph',
13
- language: 'typescript',
14
- builtAt: '2026-05-17T00:00:00Z',
15
- functions: {
16
- foo: [
17
- {
18
- bodyHash: 'h1',
19
- simpleName: 'foo',
20
- qualifiedName: 'pkg.foo',
21
- filePath: 'packages/x/src/foo.ts',
22
- line: 1,
23
- column: 0,
24
- endLine: 5,
25
- kind: 'function-declaration',
26
- params: [],
27
- returnType: null,
28
- enclosingClass: null,
29
- decorators: [],
30
- visibility: 'exported',
31
- inTestFile: false,
32
- definedInGenerated: false,
33
- calls: [],
34
- },
35
- ],
36
- },
37
- };
38
- describe('generateDashboardHtml — graph catalog wiring', () => {
39
- it('emits no id="graph-catalog" block when graphCatalog is null', () => {
40
- const html = generateDashboardHtml({ sessions: [], graphCatalog: null });
41
- expect(html).not.toContain('id="graph-catalog"');
42
- });
43
- it('emits the graph-catalog blob and is parseable JSON when supplied', () => {
44
- const html = generateDashboardHtml({ sessions: [], graphCatalog: minimalCatalog });
45
- expect(html).toContain('id="graph-catalog"');
46
- // Pull out the blob and re-parse it.
47
- const m = /<script type="application\/json" id="graph-catalog">([\s\S]*?)<\/script>/.exec(html);
48
- expect(m).not.toBeNull();
49
- const blob = m[1];
50
- // Reverse the < / > escape that escapeForScriptContext applies.
51
- const unescaped = blob.replaceAll(String.raw `<`, '<').replaceAll(String.raw `>`, '>');
52
- const parsed = JSON.parse(unescaped);
53
- expect(parsed.version).toBe('2.0');
54
- expect(Object.keys(parsed.functions)).toContain('foo');
55
- });
56
- it('embeds EDITOR_PROTOCOL = null when no editorProtocol is supplied', () => {
57
- const html = generateDashboardHtml({ sessions: [], graphCatalog: null });
58
- expect(html).toContain('const EDITOR_PROTOCOL = null;');
59
- });
60
- it('embeds EDITOR_PROTOCOL as a JS string constant when supplied', () => {
61
- const html = generateDashboardHtml({
62
- sessions: [],
63
- graphCatalog: minimalCatalog,
64
- editorProtocol: 'vscode',
65
- });
66
- expect(html).toContain('const EDITOR_PROTOCOL = "vscode";');
67
- });
68
- it('embeds the restructured view ids via the views[] registry', () => {
69
- const html = generateDashboardHtml({ sessions: [], graphCatalog: minimalCatalog });
70
- // The views register from the typed client bundle (L4); esbuild emits
71
- // double-quoted object values, so the marker is the bundled form.
72
- for (const id of ['graph', 'coupling', 'distribution']) {
73
- expect(html).toContain(`id: "${id}"`);
74
- }
75
- });
76
- });
77
- //# sourceMappingURL=dashboard-generator-graph-catalog.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard-generator-graph-catalog.test.js","sourceRoot":"","sources":["../../src/__tests__/dashboard-generator-graph-catalog.test.ts"],"names":[],"mappings":"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"}
@@ -1,13 +0,0 @@
1
- /**
2
- * Offline-render guarantee for the Graph view.
3
- *
4
- * Playwright is not part of this package's toolchain, so rather than boot a
5
- * real browser this test asserts the offline property at the artifact
6
- * level: a generated report inlines the Cytoscape renderer and the
7
- * projected view-model with ZERO external `<script src>` references, so it
8
- * renders the Graph view with the network disabled ("open an archived
9
- * report on a plane"). The only permitted external reference is the Google
10
- * Fonts stylesheet `<link>` the report already carried before this work.
11
- */
12
- export {};
13
- //# sourceMappingURL=dashboard-graph-offline.integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard-graph-offline.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dashboard-graph-offline.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -1,57 +0,0 @@
1
- /**
2
- * Offline-render guarantee for the Graph view.
3
- *
4
- * Playwright is not part of this package's toolchain, so rather than boot a
5
- * real browser this test asserts the offline property at the artifact
6
- * level: a generated report inlines the Cytoscape renderer and the
7
- * projected view-model with ZERO external `<script src>` references, so it
8
- * renders the Graph view with the network disabled ("open an archived
9
- * report on a plane"). The only permitted external reference is the Google
10
- * Fonts stylesheet `<link>` the report already carried before this work.
11
- */
12
- import { readFileSync } from 'node:fs';
13
- import { dirname, join } from 'node:path';
14
- import { fileURLToPath } from 'node:url';
15
- import { describe, expect, it } from 'vitest';
16
- import { generateDashboardHtml } from '../generator.js';
17
- const HERE = dirname(fileURLToPath(import.meta.url));
18
- function loadFixture() {
19
- const candidates = [
20
- join(HERE, 'fixtures', 'catalog-small.json'),
21
- join(HERE, '..', '..', 'src', '__tests__', 'fixtures', 'catalog-small.json'),
22
- ];
23
- for (const p of candidates) {
24
- try {
25
- return JSON.parse(readFileSync(p, 'utf8'));
26
- }
27
- catch {
28
- // next
29
- }
30
- }
31
- throw new Error('catalog-small.json fixture not found');
32
- }
33
- describe('Graph view — offline render guarantee', () => {
34
- const html = generateDashboardHtml({ sessions: [], graphCatalog: loadFixture() });
35
- it('inlines the Cytoscape renderer (no CDN fetch)', () => {
36
- expect(html).toContain('cytoscape');
37
- expect(html).toContain('cytoscapeDagre');
38
- });
39
- it('embeds the projected graph-view-model blob', () => {
40
- expect(html).toContain('id="graph-view-model"');
41
- });
42
- it('registers the Graph view', () => {
43
- // The view registers from the typed client bundle (L4) — esbuild emits
44
- // double-quoted object keys/values, so the marker is the bundled form.
45
- expect(html).toContain('id: "graph"');
46
- expect(html).toContain('code-paths-graph-canvas');
47
- });
48
- it('has no external <script src> references (renders fully offline)', () => {
49
- const scriptSrc = /<script[^>]*\ssrc=["'][^"']+["']/gi;
50
- const matches = html.match(scriptSrc) ?? [];
51
- expect(matches).toHaveLength(0);
52
- });
53
- it('has no cytoscape CDN URL', () => {
54
- expect(/https?:\/\/[^"']*cytoscape/i.test(html)).toBe(false);
55
- });
56
- });
57
- //# sourceMappingURL=dashboard-graph-offline.integration.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard-graph-offline.integration.test.js","sourceRoot":"","sources":["../../src/__tests__/dashboard-graph-offline.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAIxD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErD,SAAS,WAAW;IAClB,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,oBAAoB,CAAC;QAC5C,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,oBAAoB,CAAC;KAC7E,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAiB,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC;AAED,QAAQ,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACrD,MAAM,IAAI,GAAG,qBAAqB,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAElF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,uEAAuE;QACvE,uEAAuE;QACvE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,SAAS,GAAG,oCAAoC,CAAC;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,17 +0,0 @@
1
- /**
2
- * Tarjan SCC primitives — `buildAdjacency` and `tarjanSccIds`.
3
- *
4
- * `graph-view-model.test.ts` exercises the SCC pass end-to-end through the
5
- * projector, but only along the "happy" structural paths (clean two-package
6
- * cycles, self-loops). These tests drive the lower-level primitives directly
7
- * to lock the defensive/edge branches the projector never produces:
8
- *
9
- * - `buildAdjacency`: an edge whose `source` is not a known node (dropped),
10
- * and a duplicate edge (deduped — `includes` guard).
11
- * - `tarjanSccIds`: a node id with no adjacency entry (the `?? []` fallback),
12
- * a cross-edge into an already-finished node NOT on the stack (the
13
- * `onStack` false branch), a self-loop singleton (cyclic), and a trivial
14
- * singleton (omitted → null).
15
- */
16
- export {};
17
- //# sourceMappingURL=dashboard-graph-scc.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard-graph-scc.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dashboard-graph-scc.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -1,89 +0,0 @@
1
- /**
2
- * Tarjan SCC primitives — `buildAdjacency` and `tarjanSccIds`.
3
- *
4
- * `graph-view-model.test.ts` exercises the SCC pass end-to-end through the
5
- * projector, but only along the "happy" structural paths (clean two-package
6
- * cycles, self-loops). These tests drive the lower-level primitives directly
7
- * to lock the defensive/edge branches the projector never produces:
8
- *
9
- * - `buildAdjacency`: an edge whose `source` is not a known node (dropped),
10
- * and a duplicate edge (deduped — `includes` guard).
11
- * - `tarjanSccIds`: a node id with no adjacency entry (the `?? []` fallback),
12
- * a cross-edge into an already-finished node NOT on the stack (the
13
- * `onStack` false branch), a self-loop singleton (cyclic), and a trivial
14
- * singleton (omitted → null).
15
- */
16
- import { describe, expect, it } from 'vitest';
17
- import { buildAdjacency, tarjanSccIds } from '../code-paths/graph-scc.js';
18
- describe('buildAdjacency', () => {
19
- it('builds source → unique target lists for known nodes', () => {
20
- const adj = buildAdjacency([{ id: 'a' }, { id: 'b' }, { id: 'c' }], [
21
- { source: 'a', target: 'b' },
22
- { source: 'a', target: 'c' },
23
- ]);
24
- expect(adj.get('a')).toEqual(['b', 'c']);
25
- expect(adj.get('b')).toEqual([]);
26
- expect(adj.get('c')).toEqual([]);
27
- });
28
- it('drops an edge whose source is not a registered node (no entry to push into)', () => {
29
- // `ghost` was never declared as a node → adjacency.get returns undefined →
30
- // the `out &&` guard short-circuits and the edge is silently dropped.
31
- const adj = buildAdjacency([{ id: 'a' }], [{ source: 'ghost', target: 'a' }]);
32
- expect(adj.has('ghost')).toBe(false);
33
- expect(adj.get('a')).toEqual([]);
34
- // No stray key was created for the unknown source.
35
- expect([...adj.keys()]).toEqual(['a']);
36
- });
37
- it('dedupes a repeated edge (includes guard) so adjacency stays unique', () => {
38
- const adj = buildAdjacency([{ id: 'a' }, { id: 'b' }], [
39
- { source: 'a', target: 'b' },
40
- { source: 'a', target: 'b' },
41
- ]);
42
- expect(adj.get('a')).toEqual(['b']);
43
- });
44
- });
45
- describe('tarjanSccIds', () => {
46
- it('returns an empty map when there are no cycles (all trivial singletons omitted)', () => {
47
- const nodes = ['a', 'b'];
48
- const adj = buildAdjacency([{ id: 'a' }, { id: 'b' }], [{ source: 'a', target: 'b' }]);
49
- const scc = tarjanSccIds(nodes, adj);
50
- expect(scc.size).toBe(0);
51
- });
52
- it('tolerates a node id with no adjacency entry (the `?? []` fallback)', () => {
53
- // 'lonely' is in the node list but the adjacency map has no key for it.
54
- const adj = new Map([['a', []]]);
55
- const scc = tarjanSccIds(['lonely', 'a'], adj);
56
- // No cycle → empty, and crucially it does not throw on the missing entry.
57
- expect(scc.size).toBe(0);
58
- });
59
- it('flags a two-node cycle with a shared, deterministic sccId', () => {
60
- const adj = buildAdjacency([{ id: 'a' }, { id: 'b' }], [
61
- { source: 'a', target: 'b' },
62
- { source: 'b', target: 'a' },
63
- ]);
64
- const scc = tarjanSccIds(['a', 'b'], adj);
65
- expect(scc.get('a')).toBe('scc:a'); // smallest member id
66
- expect(scc.get('a')).toBe(scc.get('b'));
67
- });
68
- it('flags a self-loop singleton as cyclic but omits a trivial singleton', () => {
69
- const adj = buildAdjacency([{ id: 'self' }, { id: 'plain' }], [{ source: 'self', target: 'self' }]);
70
- const scc = tarjanSccIds(['self', 'plain'], adj);
71
- expect(scc.get('self')).toBe('scc:self');
72
- expect(scc.has('plain')).toBe(false);
73
- });
74
- it('handles a cross-edge into an already-finished node not on the stack (onStack false branch)', () => {
75
- // Diamond: a→b, a→c, b→d, c→d. When `a` scans `c` after `b`'s subtree
76
- // (including d) has fully closed, d is visited but NO LONGER on the stack
77
- // — exercising the `onStack.has(w)` false branch in scanSuccessors. No
78
- // node is in a cycle, so the result is empty but must not misclassify.
79
- const adj = buildAdjacency([{ id: 'a' }, { id: 'b' }, { id: 'c' }, { id: 'd' }], [
80
- { source: 'a', target: 'b' },
81
- { source: 'a', target: 'c' },
82
- { source: 'b', target: 'd' },
83
- { source: 'c', target: 'd' },
84
- ]);
85
- const scc = tarjanSccIds(['a', 'b', 'c', 'd'], adj);
86
- expect(scc.size).toBe(0);
87
- });
88
- });
89
- //# sourceMappingURL=dashboard-graph-scc.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard-graph-scc.test.js","sourceRoot":"","sources":["../../src/__tests__/dashboard-graph-scc.test.ts"],"names":[],"mappings":"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"}
@@ -1,12 +0,0 @@
1
- /**
2
- * View-model projection — `projectCatalogToGraphViewModel`.
3
- *
4
- * The projector aggregates the function call graph up to PACKAGE granularity
5
- * (item 10): one node per package, one edge per directed package→package
6
- * coupling with a call-count weight. These tests lock the schema, the
7
- * package-attribution (mirroring `pkgOf`), the weight accounting, the
8
- * totalCoupling sizing input, the external-target drop, the cross-package SCC
9
- * pass, and the byte budget.
10
- */
11
- export {};
12
- //# sourceMappingURL=dashboard-graph-view-model.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard-graph-view-model.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/dashboard-graph-view-model.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -1,331 +0,0 @@
1
- /**
2
- * View-model projection — `projectCatalogToGraphViewModel`.
3
- *
4
- * The projector aggregates the function call graph up to PACKAGE granularity
5
- * (item 10): one node per package, one edge per directed package→package
6
- * coupling with a call-count weight. These tests lock the schema, the
7
- * package-attribution (mirroring `pkgOf`), the weight accounting, the
8
- * totalCoupling sizing input, the external-target drop, the cross-package SCC
9
- * pass, and the byte budget.
10
- */
11
- import { readFileSync } from 'node:fs';
12
- import { dirname, join } from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
- import { describe, expect, it } from 'vitest';
15
- import { GraphViewModelError, packageOf, projectCatalogToGraphViewModel, } from '../code-paths/graph-view-model.js';
16
- const HERE = dirname(fileURLToPath(import.meta.url));
17
- function loadFixture() {
18
- const candidates = [
19
- join(HERE, 'fixtures', 'catalog-small.json'),
20
- join(HERE, '..', '..', 'src', '__tests__', 'fixtures', 'catalog-small.json'),
21
- ];
22
- for (const p of candidates) {
23
- try {
24
- return JSON.parse(readFileSync(p, 'utf8'));
25
- }
26
- catch {
27
- // try next candidate
28
- }
29
- }
30
- throw new Error('catalog-small.json fixture not found');
31
- }
32
- function makeOcc(over) {
33
- return {
34
- qualifiedName: over.simpleName,
35
- filePath: 'packages/x/src/x.ts',
36
- line: 1,
37
- column: 0,
38
- endLine: 5,
39
- kind: 'function-declaration',
40
- params: [],
41
- returnType: null,
42
- enclosingClass: null,
43
- decorators: [],
44
- visibility: 'exported',
45
- inTestFile: false,
46
- definedInGenerated: false,
47
- calls: [],
48
- ...over,
49
- };
50
- }
51
- /** A two-package catalog: package "a" (a1→a2 internal, a1→b1 cross) and
52
- * package "b" (b1→a1 cross — forms an a↔b cycle). */
53
- function twoPackageCatalog() {
54
- return {
55
- version: '3.0',
56
- tool: 'graph',
57
- language: 'typescript',
58
- builtAt: 'now',
59
- functions: {
60
- a1: [
61
- makeOcc({
62
- bodyHash: 'a1',
63
- simpleName: 'a1',
64
- package: '@scope/pkg-a',
65
- filePath: 'packages/pkg-a/src/a1.ts',
66
- calls: [
67
- {
68
- to: ['a2', 'b1'],
69
- line: 1,
70
- column: 0,
71
- resolution: 'static',
72
- confidence: 'high',
73
- text: 'c',
74
- },
75
- ],
76
- }),
77
- ],
78
- a2: [
79
- makeOcc({
80
- bodyHash: 'a2',
81
- simpleName: 'a2',
82
- package: '@scope/pkg-a',
83
- filePath: 'packages/pkg-a/src/a2.ts',
84
- calls: [
85
- {
86
- to: ['ext'],
87
- line: 2,
88
- column: 0,
89
- resolution: 'static',
90
- confidence: 'high',
91
- text: 'c',
92
- },
93
- ],
94
- }),
95
- ],
96
- b1: [
97
- makeOcc({
98
- bodyHash: 'b1',
99
- simpleName: 'b1',
100
- package: '@scope/pkg-b',
101
- filePath: 'packages/pkg-b/src/b1.ts',
102
- calls: [
103
- { to: ['a1'], line: 3, column: 0, resolution: 'static', confidence: 'high', text: 'c' },
104
- ],
105
- }),
106
- ],
107
- },
108
- };
109
- }
110
- describe('packageOf', () => {
111
- it('prefers the build-stamped package, scope-stripped', () => {
112
- expect(packageOf(makeOcc({ bodyHash: 'h', simpleName: 'f', package: '@opensip-cli/lang-typescript' }))).toBe('lang-typescript');
113
- });
114
- it('falls back to the path heuristic when package is absent', () => {
115
- expect(packageOf(makeOcc({ bodyHash: 'h', simpleName: 'f', filePath: 'packages/widget/src/x.ts' }))).toBe('widget');
116
- });
117
- it('returns <unknown> for an unattributable path', () => {
118
- expect(packageOf(makeOcc({ bodyHash: 'h', simpleName: 'f', filePath: 'random/x.ts' }))).toBe('<unknown>');
119
- });
120
- it('returns <unknown> when the occurrence is null/undefined or has no usable path', () => {
121
- expect(packageOf(null)).toBe('<unknown>');
122
- expect(packageOf(undefined)).toBe('<unknown>');
123
- expect(packageOf(makeOcc({ bodyHash: 'h', simpleName: 'f', filePath: '' }))).toBe('<unknown>');
124
- });
125
- it('ignores a non-string package value and a non-string path', () => {
126
- // package is the wrong type → fall back to the path heuristic.
127
- expect(packageOf(makeOcc({
128
- bodyHash: 'h',
129
- simpleName: 'f',
130
- package: 42,
131
- filePath: 'packages/widget/src/x.ts',
132
- }))).toBe('widget');
133
- // path is the wrong type → <unknown>.
134
- expect(packageOf(makeOcc({ bodyHash: 'h', simpleName: 'f', filePath: 123 }))).toBe('<unknown>');
135
- });
136
- });
137
- describe('projectCatalogToGraphViewModel (package-level)', () => {
138
- it('throws GraphViewModelError on a missing functions map', () => {
139
- expect(() => projectCatalogToGraphViewModel({})).toThrow(GraphViewModelError);
140
- });
141
- it('projects the real fixture into a stable view-model (snapshot)', () => {
142
- const vm = projectCatalogToGraphViewModel(loadFixture());
143
- expect(vm).toMatchSnapshot();
144
- });
145
- it('carries the catalog language on the root', () => {
146
- const vm = projectCatalogToGraphViewModel(loadFixture());
147
- expect(vm.language).toBe('typescript');
148
- });
149
- it('emits PACKAGE nodes (id = label = package name) — not function nodes', () => {
150
- const vm = projectCatalogToGraphViewModel(twoPackageCatalog());
151
- const ids = vm.nodes.map((n) => n.id).sort();
152
- expect(ids).toEqual(['pkg-a', 'pkg-b']);
153
- for (const node of vm.nodes) {
154
- expect(node.id).toBe(node.label);
155
- // No function-level fields leak into the package node.
156
- expect(node).not.toHaveProperty('kind');
157
- expect(node).not.toHaveProperty('filePath');
158
- expect(node).not.toHaveProperty('bodyHash');
159
- expect(typeof node.totalCoupling).toBe('number');
160
- }
161
- });
162
- it('emits package→package edges with a call-count weight', () => {
163
- const vm = projectCatalogToGraphViewModel(twoPackageCatalog());
164
- const byKey = new Map(vm.edges.map((e) => [e.source + '->' + e.target, e]));
165
- // a1→a2 is an internal package-a call → self-loop weight 1.
166
- expect(byKey.get('pkg-a->pkg-a')?.weight).toBe(1);
167
- // a1→b1 cross edge, weight 1.
168
- expect(byKey.get('pkg-a->pkg-b')?.weight).toBe(1);
169
- // b1→a1 cross edge, weight 1.
170
- expect(byKey.get('pkg-b->pkg-a')?.weight).toBe(1);
171
- });
172
- it('aggregates multiple function calls between the same package pair into one weighted edge', () => {
173
- const catalog = {
174
- version: '3.0',
175
- tool: 'graph',
176
- language: 'typescript',
177
- builtAt: 'now',
178
- functions: {
179
- a: [
180
- makeOcc({
181
- bodyHash: 'a',
182
- simpleName: 'a',
183
- package: '@s/pa',
184
- filePath: 'packages/pa/src/a.ts',
185
- calls: [
186
- {
187
- to: ['x', 'y'],
188
- line: 1,
189
- column: 0,
190
- resolution: 'static',
191
- confidence: 'high',
192
- text: 'c',
193
- },
194
- ],
195
- }),
196
- ],
197
- x: [
198
- makeOcc({
199
- bodyHash: 'x',
200
- simpleName: 'x',
201
- package: '@s/pb',
202
- filePath: 'packages/pb/src/x.ts',
203
- }),
204
- ],
205
- y: [
206
- makeOcc({
207
- bodyHash: 'y',
208
- simpleName: 'y',
209
- package: '@s/pb',
210
- filePath: 'packages/pb/src/y.ts',
211
- }),
212
- ],
213
- },
214
- };
215
- const vm = projectCatalogToGraphViewModel(catalog);
216
- const edge = vm.edges.find((e) => e.source === 'pa' && e.target === 'pb');
217
- expect(edge?.weight).toBe(2);
218
- });
219
- it('drops calls whose target is not an in-project function (external)', () => {
220
- const vm = projectCatalogToGraphViewModel(twoPackageCatalog());
221
- // a2 calls only an external 'ext' — it must NOT create any edge.
222
- expect(vm.edges.some((e) => e.target === '<unknown>' || e.target === 'ext')).toBe(false);
223
- });
224
- it('sets totalCoupling = sum of incident edge weights (fan-in + fan-out)', () => {
225
- const vm = projectCatalogToGraphViewModel(twoPackageCatalog());
226
- for (const node of vm.nodes) {
227
- const incident = vm.edges
228
- .filter((e) => e.source === node.id || e.target === node.id)
229
- // a self-loop contributes its weight to both fan-in and fan-out.
230
- .reduce((sum, e) => sum + (e.source === node.id ? e.weight : 0) + (e.target === node.id ? e.weight : 0), 0);
231
- expect(node.totalCoupling).toBe(incident);
232
- }
233
- });
234
- it('stamps a shared non-null sccId on a cross-package cycle (a↔b)', () => {
235
- const vm = projectCatalogToGraphViewModel(twoPackageCatalog());
236
- const sccIds = new Set(vm.nodes.map((n) => n.sccId));
237
- expect(sccIds.size).toBe(1);
238
- expect([...sccIds][0]).not.toBeNull();
239
- const crossCycleEdges = vm.edges.filter((e) => e.source !== e.target &&
240
- ['pkg-a', 'pkg-b'].includes(e.source) &&
241
- ['pkg-a', 'pkg-b'].includes(e.target));
242
- for (const e of crossCycleEdges)
243
- expect(e.isCycleEdge).toBe(true);
244
- });
245
- it('does NOT flag a package as cyclic just because it has internal (self-loop) calls', () => {
246
- const catalog = {
247
- version: '3.0',
248
- tool: 'graph',
249
- language: 'typescript',
250
- builtAt: 'now',
251
- functions: {
252
- a: [
253
- makeOcc({
254
- bodyHash: 'a',
255
- simpleName: 'a',
256
- package: '@s/solo',
257
- filePath: 'packages/solo/src/a.ts',
258
- calls: [
259
- {
260
- to: ['b'],
261
- line: 1,
262
- column: 0,
263
- resolution: 'static',
264
- confidence: 'high',
265
- text: 'c',
266
- },
267
- ],
268
- }),
269
- ],
270
- b: [
271
- makeOcc({
272
- bodyHash: 'b',
273
- simpleName: 'b',
274
- package: '@s/solo',
275
- filePath: 'packages/solo/src/b.ts',
276
- }),
277
- ],
278
- },
279
- };
280
- const vm = projectCatalogToGraphViewModel(catalog);
281
- expect(vm.nodes).toHaveLength(1);
282
- expect(vm.nodes[0].sccId).toBeNull();
283
- expect(vm.edges[0].isCycleEdge).toBe(false);
284
- });
285
- it('tolerates occurrences with no calls and edges with no targets (empty-array branches)', () => {
286
- const catalog = {
287
- version: '3.0',
288
- tool: 'graph',
289
- language: 'typescript',
290
- builtAt: 'now',
291
- functions: {
292
- // No `calls` at all.
293
- a: [
294
- makeOcc({
295
- bodyHash: 'a',
296
- simpleName: 'a',
297
- package: '@s/pa',
298
- filePath: 'packages/pa/src/a.ts',
299
- }),
300
- ],
301
- // A call edge with an empty `to` array.
302
- b: [
303
- makeOcc({
304
- bodyHash: 'b',
305
- simpleName: 'b',
306
- package: '@s/pa',
307
- filePath: 'packages/pa/src/b.ts',
308
- calls: [
309
- { to: [], line: 1, column: 0, resolution: 'static', confidence: 'high', text: 'c' },
310
- ],
311
- }),
312
- ],
313
- // An empty occurrences list for a function name.
314
- c: [],
315
- },
316
- };
317
- const vm = projectCatalogToGraphViewModel(catalog);
318
- expect(vm.nodes.map((n) => n.id)).toEqual(['pa']);
319
- expect(vm.edges).toHaveLength(0);
320
- expect(vm.nodes[0].totalCoupling).toBe(0);
321
- expect(vm.nodes[0].sccId).toBeNull();
322
- });
323
- it('keeps the JSON within a generous per-node/edge byte budget', () => {
324
- const vm = projectCatalogToGraphViewModel(loadFixture());
325
- const bytes = JSON.stringify(vm).length;
326
- // Package nodes are far slimmer than function nodes were.
327
- const budget = (vm.nodes.length * 120 + vm.edges.length * 80) * 1.5 + 512;
328
- expect(bytes).toBeLessThan(budget);
329
- });
330
- });
331
- //# sourceMappingURL=dashboard-graph-view-model.test.js.map