@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.
- package/README.md +2 -2
- package/dist/client-bundle.generated.d.ts.map +1 -1
- package/dist/client-bundle.generated.js +1 -1
- package/dist/client-bundle.generated.js.map +1 -1
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +4 -3
- package/dist/generator.js.map +1 -1
- package/dist/report-cup-icon.d.ts +17 -0
- package/dist/report-cup-icon.d.ts.map +1 -0
- package/dist/report-cup-icon.js +34 -0
- package/dist/report-cup-icon.js.map +1 -0
- package/package.json +3 -3
- package/dist/__tests__/catalog-provenance.test.d.ts +0 -11
- package/dist/__tests__/catalog-provenance.test.d.ts.map +0 -1
- package/dist/__tests__/catalog-provenance.test.js +0 -112
- package/dist/__tests__/catalog-provenance.test.js.map +0 -1
- package/dist/__tests__/coupling-attribution.test.d.ts +0 -9
- package/dist/__tests__/coupling-attribution.test.d.ts.map +0 -1
- package/dist/__tests__/coupling-attribution.test.js +0 -95
- package/dist/__tests__/coupling-attribution.test.js.map +0 -1
- package/dist/__tests__/dashboard-bundle-weight.test.d.ts +0 -23
- package/dist/__tests__/dashboard-bundle-weight.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-bundle-weight.test.js +0 -80
- package/dist/__tests__/dashboard-bundle-weight.test.js.map +0 -1
- package/dist/__tests__/dashboard-cell-containment.test.d.ts +0 -2
- package/dist/__tests__/dashboard-cell-containment.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-cell-containment.test.js +0 -52
- package/dist/__tests__/dashboard-cell-containment.test.js.map +0 -1
- package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts +0 -10
- package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-cytoscape-vendor.test.js +0 -40
- package/dist/__tests__/dashboard-cytoscape-vendor.test.js.map +0 -1
- package/dist/__tests__/dashboard-editor-link.test.d.ts +0 -14
- package/dist/__tests__/dashboard-editor-link.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-editor-link.test.js +0 -87
- package/dist/__tests__/dashboard-editor-link.test.js.map +0 -1
- package/dist/__tests__/dashboard-el.test.d.ts +0 -18
- package/dist/__tests__/dashboard-el.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-el.test.js +0 -90
- package/dist/__tests__/dashboard-el.test.js.map +0 -1
- package/dist/__tests__/dashboard-filters.test.d.ts +0 -9
- package/dist/__tests__/dashboard-filters.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-filters.test.js +0 -87
- package/dist/__tests__/dashboard-filters.test.js.map +0 -1
- package/dist/__tests__/dashboard-function-card-singleton.test.d.ts +0 -9
- package/dist/__tests__/dashboard-function-card-singleton.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-function-card-singleton.test.js +0 -75
- package/dist/__tests__/dashboard-function-card-singleton.test.js.map +0 -1
- package/dist/__tests__/dashboard-function-card.test.d.ts +0 -10
- package/dist/__tests__/dashboard-function-card.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-function-card.test.js +0 -439
- package/dist/__tests__/dashboard-function-card.test.js.map +0 -1
- package/dist/__tests__/dashboard-function-row.test.d.ts +0 -11
- package/dist/__tests__/dashboard-function-row.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-function-row.test.js +0 -102
- package/dist/__tests__/dashboard-function-row.test.js.map +0 -1
- package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts +0 -9
- package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-generator-graph-catalog.test.js +0 -77
- package/dist/__tests__/dashboard-generator-graph-catalog.test.js.map +0 -1
- package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts +0 -13
- package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-graph-offline.integration.test.js +0 -57
- package/dist/__tests__/dashboard-graph-offline.integration.test.js.map +0 -1
- package/dist/__tests__/dashboard-graph-scc.test.d.ts +0 -17
- package/dist/__tests__/dashboard-graph-scc.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-graph-scc.test.js +0 -89
- package/dist/__tests__/dashboard-graph-scc.test.js.map +0 -1
- package/dist/__tests__/dashboard-graph-view-model.test.d.ts +0 -12
- package/dist/__tests__/dashboard-graph-view-model.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-graph-view-model.test.js +0 -331
- package/dist/__tests__/dashboard-graph-view-model.test.js.map +0 -1
- package/dist/__tests__/dashboard-help-drawer.test.d.ts +0 -9
- package/dist/__tests__/dashboard-help-drawer.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-help-drawer.test.js +0 -95
- package/dist/__tests__/dashboard-help-drawer.test.js.map +0 -1
- package/dist/__tests__/dashboard-html.test.d.ts +0 -2
- package/dist/__tests__/dashboard-html.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-html.test.js +0 -169
- package/dist/__tests__/dashboard-html.test.js.map +0 -1
- package/dist/__tests__/dashboard-indexes.test.d.ts +0 -11
- package/dist/__tests__/dashboard-indexes.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-indexes.test.js +0 -214
- package/dist/__tests__/dashboard-indexes.test.js.map +0 -1
- package/dist/__tests__/dashboard-pagination.test.d.ts +0 -15
- package/dist/__tests__/dashboard-pagination.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-pagination.test.js +0 -140
- package/dist/__tests__/dashboard-pagination.test.js.map +0 -1
- package/dist/__tests__/dashboard-path-utils.test.d.ts +0 -13
- package/dist/__tests__/dashboard-path-utils.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-path-utils.test.js +0 -62
- package/dist/__tests__/dashboard-path-utils.test.js.map +0 -1
- package/dist/__tests__/dashboard-search.test.d.ts +0 -10
- package/dist/__tests__/dashboard-search.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-search.test.js +0 -52
- package/dist/__tests__/dashboard-search.test.js.map +0 -1
- package/dist/__tests__/dashboard-sessions.test.d.ts +0 -14
- package/dist/__tests__/dashboard-sessions.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-sessions.test.js +0 -400
- package/dist/__tests__/dashboard-sessions.test.js.map +0 -1
- package/dist/__tests__/dashboard-trace.test.d.ts +0 -16
- package/dist/__tests__/dashboard-trace.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-trace.test.js +0 -170
- package/dist/__tests__/dashboard-trace.test.js.map +0 -1
- package/dist/__tests__/dashboard-validation.integration.test.d.ts +0 -14
- package/dist/__tests__/dashboard-validation.integration.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-validation.integration.test.js +0 -221
- package/dist/__tests__/dashboard-validation.integration.test.js.map +0 -1
- package/dist/__tests__/dashboard-view-conformance.test.d.ts +0 -12
- package/dist/__tests__/dashboard-view-conformance.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-view-conformance.test.js +0 -52
- package/dist/__tests__/dashboard-view-conformance.test.js.map +0 -1
- package/dist/__tests__/dashboard-view-coupling.test.d.ts +0 -7
- package/dist/__tests__/dashboard-view-coupling.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-view-coupling.test.js +0 -360
- package/dist/__tests__/dashboard-view-coupling.test.js.map +0 -1
- package/dist/__tests__/dashboard-view-distribution.test.d.ts +0 -10
- package/dist/__tests__/dashboard-view-distribution.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-view-distribution.test.js +0 -249
- package/dist/__tests__/dashboard-view-distribution.test.js.map +0 -1
- package/dist/__tests__/dashboard-view-graph.test.d.ts +0 -13
- package/dist/__tests__/dashboard-view-graph.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-view-graph.test.js +0 -299
- package/dist/__tests__/dashboard-view-graph.test.js.map +0 -1
- package/dist/__tests__/dashboard-view-template.test.d.ts +0 -20
- package/dist/__tests__/dashboard-view-template.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard-view-template.test.js +0 -206
- package/dist/__tests__/dashboard-view-template.test.js.map +0 -1
- package/dist/__tests__/dashboard.test.d.ts +0 -2
- package/dist/__tests__/dashboard.test.d.ts.map +0 -1
- package/dist/__tests__/dashboard.test.js +0 -129
- package/dist/__tests__/dashboard.test.js.map +0 -1
- package/dist/__tests__/graph-tab.test.d.ts +0 -10
- package/dist/__tests__/graph-tab.test.d.ts.map +0 -1
- package/dist/__tests__/graph-tab.test.js +0 -78
- package/dist/__tests__/graph-tab.test.js.map +0 -1
- package/dist/__tests__/yagni-tab.test.d.ts +0 -13
- package/dist/__tests__/yagni-tab.test.d.ts.map +0 -1
- package/dist/__tests__/yagni-tab.test.js +0 -85
- package/dist/__tests__/yagni-tab.test.js.map +0 -1
- package/dist/client/el.d.ts +0 -17
- package/dist/client/el.d.ts.map +0 -1
- package/dist/client/el.js +0 -36
- package/dist/client/el.js.map +0 -1
- package/dist/code-paths/__tests__/views-registry.test.d.ts +0 -18
- package/dist/code-paths/__tests__/views-registry.test.d.ts.map +0 -1
- package/dist/code-paths/__tests__/views-registry.test.js +0 -41
- 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
|