@usefragments/core 1.0.0
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/LICENSE +21 -0
- package/dist/chunk-243QYRUF.js +2145 -0
- package/dist/chunk-243QYRUF.js.map +1 -0
- package/dist/chunk-3LLRNCPX.js +111 -0
- package/dist/chunk-3LLRNCPX.js.map +1 -0
- package/dist/chunk-EIYNNS77.js +48 -0
- package/dist/chunk-EIYNNS77.js.map +1 -0
- package/dist/chunk-JNBFJ34I.js +326 -0
- package/dist/chunk-JNBFJ34I.js.map +1 -0
- package/dist/chunk-R3X7UBHU.js +228 -0
- package/dist/chunk-R3X7UBHU.js.map +1 -0
- package/dist/chunk-SH4KPIYH.js +393 -0
- package/dist/chunk-SH4KPIYH.js.map +1 -0
- package/dist/chunk-V4VQB57N.js +56 -0
- package/dist/chunk-V4VQB57N.js.map +1 -0
- package/dist/chunk-X34IA4LR.js +319 -0
- package/dist/chunk-X34IA4LR.js.map +1 -0
- package/dist/chunk-YF65VYRY.js +493 -0
- package/dist/chunk-YF65VYRY.js.map +1 -0
- package/dist/codes/index.d.ts +40 -0
- package/dist/codes/index.js +23 -0
- package/dist/codes/index.js.map +1 -0
- package/dist/compiled-types/index.d.ts +5 -0
- package/dist/compiled-types/index.js +1 -0
- package/dist/compiled-types/index.js.map +1 -0
- package/dist/generate/index.d.ts +47 -0
- package/dist/generate/index.js +11 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/governance-BOa3KyiJ.d.ts +3279 -0
- package/dist/graph/index.d.ts +66 -0
- package/dist/graph/index.js +17 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/index.d.ts +11388 -0
- package/dist/index.js +8711 -0
- package/dist/index.js.map +1 -0
- package/dist/package-identity.d.ts +42 -0
- package/dist/package-identity.js +23 -0
- package/dist/package-identity.js.map +1 -0
- package/dist/preview/index.d.ts +484 -0
- package/dist/preview/index.js +1129 -0
- package/dist/preview/index.js.map +1 -0
- package/dist/react-types.d.ts +22 -0
- package/dist/react-types.js +1 -0
- package/dist/react-types.js.map +1 -0
- package/dist/registry.d.ts +2367 -0
- package/dist/registry.js +62 -0
- package/dist/registry.js.map +1 -0
- package/dist/schemas/index.d.ts +2584 -0
- package/dist/schemas/index.js +55 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/severity.d.ts +18 -0
- package/dist/severity.js +27 -0
- package/dist/severity.js.map +1 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +63 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/topology/index.d.ts +48 -0
- package/dist/topology/index.js +7 -0
- package/dist/topology/index.js.map +1 -0
- package/dist/types-xJ2xyp_G.d.ts +119 -0
- package/package.json +120 -0
- package/src/__tests__/contract-parser.test.ts +614 -0
- package/src/__tests__/dtcg-generators.test.ts +439 -0
- package/src/__tests__/dtcg-parser.test.ts +884 -0
- package/src/__tests__/preview-runtime.test.tsx +111 -0
- package/src/__tests__/schema.test.ts +31 -0
- package/src/__tests__/severity.test.ts +34 -0
- package/src/__tests__/style-comparison.test.ts +662 -0
- package/src/__tests__/tokens-compat.test.ts +51 -0
- package/src/agent-format.test.ts +105 -0
- package/src/agent-format.ts +141 -0
- package/src/bundle.ts +149 -0
- package/src/codes/__tests__/codes.test.ts +73 -0
- package/src/codes/codes.ts +463 -0
- package/src/codes/index.ts +7 -0
- package/src/codes/source-finding-bridge.ts +113 -0
- package/src/compiled-types/index.ts +293 -0
- package/src/component-contract.ts +231 -0
- package/src/component-discovery.ts +80 -0
- package/src/composition.test.ts +262 -0
- package/src/composition.ts +318 -0
- package/src/config-paths.test.ts +78 -0
- package/src/config-paths.ts +47 -0
- package/src/config.ts +18 -0
- package/src/conform-prove.test.ts +274 -0
- package/src/conform-prove.ts +326 -0
- package/src/conform.ts +160 -0
- package/src/constants.ts +114 -0
- package/src/context.ts +2 -0
- package/src/contract/fixture.ts +110 -0
- package/src/contract/hash.test.ts +86 -0
- package/src/contract/hash.ts +195 -0
- package/src/contract/index.ts +43 -0
- package/src/contract/preimage.test.ts +254 -0
- package/src/contract/preimage.ts +365 -0
- package/src/contract/stamp.test.ts +100 -0
- package/src/contract/stamp.ts +105 -0
- package/src/contract-parser.ts +141 -0
- package/src/defineFragment.test.ts +257 -0
- package/src/defineFragment.ts +293 -0
- package/src/dtcg-generators.ts +324 -0
- package/src/dtcg-parser.ts +1 -0
- package/src/dtcg.ts +237 -0
- package/src/facts/__snapshots__/facts.test.ts.snap +124 -0
- package/src/facts/builders.ts +835 -0
- package/src/facts/compile.ts +422 -0
- package/src/facts/fact-index.ts +394 -0
- package/src/facts/facts.test.ts +494 -0
- package/src/facts/ids.ts +125 -0
- package/src/facts/index.ts +119 -0
- package/src/facts/types.ts +614 -0
- package/src/figma.ts +263 -0
- package/src/fragment-types.ts +214 -0
- package/src/generate/generate.test.ts +594 -0
- package/src/generate/index.ts +438 -0
- package/src/governance-integrity.test.ts +186 -0
- package/src/governance-integrity.ts +282 -0
- package/src/governance.test.ts +300 -0
- package/src/governance.ts +605 -0
- package/src/governance.type-test.ts +75 -0
- package/src/graph/engine.ts +446 -0
- package/src/graph/graph.test.ts +746 -0
- package/src/graph/index.ts +32 -0
- package/src/graph/serialization.ts +55 -0
- package/src/graph/types.ts +178 -0
- package/src/index.ts +891 -0
- package/src/local-canonical.ts +102 -0
- package/src/mcp-snapshot.ts +222 -0
- package/src/package-identity-match.ts +33 -0
- package/src/package-identity.generated.ts +233 -0
- package/src/package-identity.test.ts +99 -0
- package/src/package-identity.ts +157 -0
- package/src/performance-presets.ts +142 -0
- package/src/preview/comparison-artifact.test.ts +228 -0
- package/src/preview/comparison-artifact.ts +181 -0
- package/src/preview/comparison-context.ts +93 -0
- package/src/preview/comparison-evidence.ts +112 -0
- package/src/preview/comparison-format.ts +21 -0
- package/src/preview/comparison-presentation.ts +187 -0
- package/src/preview/comparison-references.test.ts +430 -0
- package/src/preview/comparison-references.ts +72 -0
- package/src/preview/comparison-score.ts +59 -0
- package/src/preview/comparison-styles.ts +197 -0
- package/src/preview/comparison.test.ts +559 -0
- package/src/preview/comparison.ts +488 -0
- package/src/preview/index.ts +10 -0
- package/src/preview/types.ts +309 -0
- package/src/preview/validation.test.ts +290 -0
- package/src/preview/validation.ts +356 -0
- package/src/preview-runtime.tsx +144 -0
- package/src/raw-html-canonical.test.ts +41 -0
- package/src/raw-html-canonical.ts +179 -0
- package/src/react-types.ts +21 -0
- package/src/registry-install-plan.ts +344 -0
- package/src/registry.test.ts +245 -0
- package/src/registry.ts +369 -0
- package/src/rules/a11y-required-accessible-name.ts +93 -0
- package/src/rules/color-math.test.ts +72 -0
- package/src/rules/color-math.ts +192 -0
- package/src/rules/components-forbidden-prop-value.ts +108 -0
- package/src/rules/components-prefer-library.ts +943 -0
- package/src/rules/components-unknown-prop.ts +94 -0
- package/src/rules/composition-pattern-schema.ts +125 -0
- package/src/rules/composition-pattern.test.ts +320 -0
- package/src/rules/composition-pattern.ts +271 -0
- package/src/rules/emit-gate.test.ts +42 -0
- package/src/rules/emit-gate.ts +60 -0
- package/src/rules/finding.ts +54 -0
- package/src/rules/index.ts +235 -0
- package/src/rules/jsx-preferred-component.ts +68 -0
- package/src/rules/jsx-preferred-import-path.ts +67 -0
- package/src/rules/presets.ts +57 -0
- package/src/rules/props-invalid-value.ts +115 -0
- package/src/rules/rules.test.ts +3636 -0
- package/src/rules/spacing-resolution.ts +184 -0
- package/src/rules/styles-no-raw-color.ts +329 -0
- package/src/rules/styles-no-raw-dimensions.ts +357 -0
- package/src/rules/styles-no-raw-spacing.ts +236 -0
- package/src/rules/styles-no-raw-typography.ts +237 -0
- package/src/rules/tailwind-arbitrary-color.test.ts +128 -0
- package/src/rules/tailwind-arbitrary-color.ts +93 -0
- package/src/rules/tailwind-arbitrary-spacing.test.ts +157 -0
- package/src/rules/tailwind-arbitrary-spacing.ts +120 -0
- package/src/rules/tailwind-forbidden-palette.test.ts +116 -0
- package/src/rules/tailwind-forbidden-palette.ts +107 -0
- package/src/rules/tailwind-off-scale-spacing-token.test.ts +100 -0
- package/src/rules/tailwind-off-scale-spacing-token.ts +108 -0
- package/src/rules/tailwind-raw-color-via-token.test.ts +108 -0
- package/src/rules/tailwind-raw-color-via-token.ts +82 -0
- package/src/rules/tailwind-rule-utils.ts +106 -0
- package/src/rules/tailwind-unknown-class.test.ts +157 -0
- package/src/rules/tailwind-unknown-class.ts +121 -0
- package/src/rules/taxonomy.test.ts +238 -0
- package/src/rules/theme-no-theme-coupled-literal.ts +132 -0
- package/src/rules/tiers.ts +111 -0
- package/src/rules/tokens-css-vars-must-be-defined.test.ts +244 -0
- package/src/rules/tokens-css-vars-must-be-defined.ts +156 -0
- package/src/rules/tokens-require-dual-fallback.ts +78 -0
- package/src/rules/types.ts +27 -0
- package/src/rules/utils.test.ts +110 -0
- package/src/rules/utils.ts +599 -0
- package/src/schema.ts +427 -0
- package/src/schemas/index.ts +275 -0
- package/src/schemas/schemas.test.ts +124 -0
- package/src/severity.ts +54 -0
- package/src/storyAdapter.test.ts +571 -0
- package/src/storyAdapter.ts +761 -0
- package/src/storyFilters.test.ts +350 -0
- package/src/storyFilters.ts +253 -0
- package/src/storybook-csf.ts +11 -0
- package/src/style-comparison.ts +423 -0
- package/src/test-utils.ts +68 -0
- package/src/token-parser.ts +2 -0
- package/src/token-types.ts +287 -0
- package/src/tokens/categories.ts +131 -0
- package/src/tokens/color.ts +193 -0
- package/src/tokens/design-token-parser.test.ts +159 -0
- package/src/tokens/design-token-parser.ts +402 -0
- package/src/tokens/dtcg.ts +428 -0
- package/src/tokens/index.ts +130 -0
- package/src/tokens/lookup.ts +19 -0
- package/src/tokens/scss.ts +171 -0
- package/src/tokens/tailwind-v4.ts +187 -0
- package/src/tokens/types.ts +43 -0
- package/src/topology/index.ts +9 -0
- package/src/topology/resolve-area.test.ts +174 -0
- package/src/topology/resolve-area.ts +215 -0
- package/src/types.ts +1057 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
// src/graph/types.ts
|
|
2
|
+
var GRAPH_EDGE_TYPES = [
|
|
3
|
+
"imports",
|
|
4
|
+
"hook-depends",
|
|
5
|
+
"renders",
|
|
6
|
+
"composes",
|
|
7
|
+
"parent-of",
|
|
8
|
+
"alternative-to",
|
|
9
|
+
"sibling-of"
|
|
10
|
+
];
|
|
11
|
+
var EDGE_TYPE_WEIGHTS = {
|
|
12
|
+
"imports": 1,
|
|
13
|
+
"hook-depends": 0.75,
|
|
14
|
+
"renders": 0.5,
|
|
15
|
+
"composes": 0.5,
|
|
16
|
+
"parent-of": 1,
|
|
17
|
+
"alternative-to": 1,
|
|
18
|
+
"sibling-of": 0.75
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// src/graph/engine.ts
|
|
22
|
+
var ComponentGraphEngine = class {
|
|
23
|
+
nodes;
|
|
24
|
+
outgoing;
|
|
25
|
+
incoming;
|
|
26
|
+
edges;
|
|
27
|
+
blockIndex;
|
|
28
|
+
health;
|
|
29
|
+
constructor(graph, blocks) {
|
|
30
|
+
this.nodes = /* @__PURE__ */ new Map();
|
|
31
|
+
this.outgoing = /* @__PURE__ */ new Map();
|
|
32
|
+
this.incoming = /* @__PURE__ */ new Map();
|
|
33
|
+
this.edges = graph.edges;
|
|
34
|
+
this.health = graph.health;
|
|
35
|
+
this.blockIndex = /* @__PURE__ */ new Map();
|
|
36
|
+
for (const node of graph.nodes) {
|
|
37
|
+
this.nodes.set(node.name, node);
|
|
38
|
+
this.outgoing.set(node.name, []);
|
|
39
|
+
this.incoming.set(node.name, []);
|
|
40
|
+
}
|
|
41
|
+
for (const edge of graph.edges) {
|
|
42
|
+
const out = this.outgoing.get(edge.source);
|
|
43
|
+
if (out) out.push(edge);
|
|
44
|
+
else this.outgoing.set(edge.source, [edge]);
|
|
45
|
+
const inc = this.incoming.get(edge.target);
|
|
46
|
+
if (inc) inc.push(edge);
|
|
47
|
+
else this.incoming.set(edge.target, [edge]);
|
|
48
|
+
}
|
|
49
|
+
if (blocks) {
|
|
50
|
+
for (const [blockName, block] of Object.entries(blocks)) {
|
|
51
|
+
for (const comp of block.components) {
|
|
52
|
+
const existing = this.blockIndex.get(comp);
|
|
53
|
+
if (existing) existing.push(blockName);
|
|
54
|
+
else this.blockIndex.set(comp, [blockName]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// -------------------------------------------------------------------------
|
|
60
|
+
// Core queries
|
|
61
|
+
// -------------------------------------------------------------------------
|
|
62
|
+
/** Get outgoing edges from a component, optionally filtered by edge type */
|
|
63
|
+
dependencies(component, edgeTypes) {
|
|
64
|
+
const edges = this.outgoing.get(component) ?? [];
|
|
65
|
+
if (!edgeTypes || edgeTypes.length === 0) return edges;
|
|
66
|
+
return edges.filter((e) => edgeTypes.includes(e.type));
|
|
67
|
+
}
|
|
68
|
+
/** Get incoming edges to a component, optionally filtered by edge type */
|
|
69
|
+
dependents(component, edgeTypes) {
|
|
70
|
+
const edges = this.incoming.get(component) ?? [];
|
|
71
|
+
if (!edgeTypes || edgeTypes.length === 0) return edges;
|
|
72
|
+
return edges.filter((e) => edgeTypes.includes(e.type));
|
|
73
|
+
}
|
|
74
|
+
/** BFS transitive closure — what's affected if this component changes */
|
|
75
|
+
impact(component, maxDepth = 3) {
|
|
76
|
+
const affected = [];
|
|
77
|
+
const visited = /* @__PURE__ */ new Set([component]);
|
|
78
|
+
const queue = [
|
|
79
|
+
{ name: component, depth: 0, path: [component] }
|
|
80
|
+
];
|
|
81
|
+
while (queue.length > 0) {
|
|
82
|
+
const current = queue.shift();
|
|
83
|
+
if (current.depth >= maxDepth) continue;
|
|
84
|
+
const deps = this.incoming.get(current.name) ?? [];
|
|
85
|
+
for (const edge of deps) {
|
|
86
|
+
if (visited.has(edge.source)) continue;
|
|
87
|
+
visited.add(edge.source);
|
|
88
|
+
const newPath = [...current.path, edge.source];
|
|
89
|
+
affected.push({
|
|
90
|
+
component: edge.source,
|
|
91
|
+
depth: current.depth + 1,
|
|
92
|
+
path: newPath,
|
|
93
|
+
edgeType: edge.type
|
|
94
|
+
});
|
|
95
|
+
queue.push({ name: edge.source, depth: current.depth + 1, path: newPath });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const affectedComponents = /* @__PURE__ */ new Set([component, ...affected.map((a) => a.component)]);
|
|
99
|
+
const affectedBlocks = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const comp of affectedComponents) {
|
|
101
|
+
const blocks = this.blockIndex.get(comp);
|
|
102
|
+
if (blocks) {
|
|
103
|
+
for (const b of blocks) affectedBlocks.add(b);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
component,
|
|
108
|
+
affected,
|
|
109
|
+
affectedBlocks: [...affectedBlocks],
|
|
110
|
+
totalAffected: affected.length
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** BFS shortest path between two components (undirected) */
|
|
114
|
+
path(from, to) {
|
|
115
|
+
if (from === to) {
|
|
116
|
+
return { found: true, path: [from], edges: [] };
|
|
117
|
+
}
|
|
118
|
+
const visited = /* @__PURE__ */ new Set([from]);
|
|
119
|
+
const queue = [
|
|
120
|
+
{ name: from, path: [from], edges: [] }
|
|
121
|
+
];
|
|
122
|
+
while (queue.length > 0) {
|
|
123
|
+
const current = queue.shift();
|
|
124
|
+
const allEdges = [
|
|
125
|
+
...this.outgoing.get(current.name) ?? [],
|
|
126
|
+
...this.incoming.get(current.name) ?? []
|
|
127
|
+
];
|
|
128
|
+
for (const edge of allEdges) {
|
|
129
|
+
const neighbor = edge.source === current.name ? edge.target : edge.source;
|
|
130
|
+
if (visited.has(neighbor)) continue;
|
|
131
|
+
visited.add(neighbor);
|
|
132
|
+
const newPath = [...current.path, neighbor];
|
|
133
|
+
const newEdges = [...current.edges, edge];
|
|
134
|
+
if (neighbor === to) {
|
|
135
|
+
return { found: true, path: newPath, edges: newEdges };
|
|
136
|
+
}
|
|
137
|
+
queue.push({ name: neighbor, path: newPath, edges: newEdges });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { found: false, path: [], edges: [] };
|
|
141
|
+
}
|
|
142
|
+
/** Connected components via BFS on undirected projection */
|
|
143
|
+
islands() {
|
|
144
|
+
const visited = /* @__PURE__ */ new Set();
|
|
145
|
+
const components = [];
|
|
146
|
+
for (const nodeName of this.nodes.keys()) {
|
|
147
|
+
if (visited.has(nodeName)) continue;
|
|
148
|
+
const island = [];
|
|
149
|
+
const queue = [nodeName];
|
|
150
|
+
visited.add(nodeName);
|
|
151
|
+
while (queue.length > 0) {
|
|
152
|
+
const current = queue.shift();
|
|
153
|
+
island.push(current);
|
|
154
|
+
const allEdges = [
|
|
155
|
+
...this.outgoing.get(current) ?? [],
|
|
156
|
+
...this.incoming.get(current) ?? []
|
|
157
|
+
];
|
|
158
|
+
for (const edge of allEdges) {
|
|
159
|
+
const neighbor = edge.source === current ? edge.target : edge.source;
|
|
160
|
+
if (!visited.has(neighbor) && this.nodes.has(neighbor)) {
|
|
161
|
+
visited.add(neighbor);
|
|
162
|
+
queue.push(neighbor);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
components.push(island.sort());
|
|
167
|
+
}
|
|
168
|
+
return components.sort((a, b) => b.length - a.length);
|
|
169
|
+
}
|
|
170
|
+
/** All components reachable within N hops (undirected) */
|
|
171
|
+
neighbors(component, maxHops = 1) {
|
|
172
|
+
const neighbors = [];
|
|
173
|
+
const visited = /* @__PURE__ */ new Set([component]);
|
|
174
|
+
const queue = [
|
|
175
|
+
{ name: component, hops: 0 }
|
|
176
|
+
];
|
|
177
|
+
while (queue.length > 0) {
|
|
178
|
+
const current = queue.shift();
|
|
179
|
+
if (current.hops >= maxHops) continue;
|
|
180
|
+
const allEdges = [
|
|
181
|
+
...this.outgoing.get(current.name) ?? [],
|
|
182
|
+
...this.incoming.get(current.name) ?? []
|
|
183
|
+
];
|
|
184
|
+
for (const edge of allEdges) {
|
|
185
|
+
const neighbor = edge.source === current.name ? edge.target : edge.source;
|
|
186
|
+
if (visited.has(neighbor)) continue;
|
|
187
|
+
visited.add(neighbor);
|
|
188
|
+
neighbors.push({
|
|
189
|
+
component: neighbor,
|
|
190
|
+
hops: current.hops + 1,
|
|
191
|
+
edgeType: edge.type
|
|
192
|
+
});
|
|
193
|
+
queue.push({ name: neighbor, hops: current.hops + 1 });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return { component, neighbors };
|
|
197
|
+
}
|
|
198
|
+
// -------------------------------------------------------------------------
|
|
199
|
+
// Design-system queries
|
|
200
|
+
// -------------------------------------------------------------------------
|
|
201
|
+
/** Get the composition tree for a compound component */
|
|
202
|
+
composition(component) {
|
|
203
|
+
const node = this.nodes.get(component);
|
|
204
|
+
const subComponents = node?.subComponents ?? [];
|
|
205
|
+
const parentEdges = (this.outgoing.get(component) ?? []).filter((e) => e.type === "parent-of");
|
|
206
|
+
const children = parentEdges.map((e) => e.target);
|
|
207
|
+
const childEdges = (this.incoming.get(component) ?? []).filter((e) => e.type === "parent-of");
|
|
208
|
+
const parent = childEdges.length > 0 ? childEdges[0].source : void 0;
|
|
209
|
+
const siblings = [];
|
|
210
|
+
if (parent) {
|
|
211
|
+
const parentOut = (this.outgoing.get(parent) ?? []).filter((e) => e.type === "parent-of");
|
|
212
|
+
for (const edge of parentOut) {
|
|
213
|
+
if (edge.target !== component) {
|
|
214
|
+
siblings.push(edge.target);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const siblingEdges = [
|
|
219
|
+
...(this.outgoing.get(component) ?? []).filter((e) => e.type === "sibling-of"),
|
|
220
|
+
...(this.incoming.get(component) ?? []).filter((e) => e.type === "sibling-of")
|
|
221
|
+
];
|
|
222
|
+
for (const edge of siblingEdges) {
|
|
223
|
+
const sib = edge.source === component ? edge.target : edge.source;
|
|
224
|
+
if (!siblings.includes(sib)) siblings.push(sib);
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
component,
|
|
228
|
+
compositionPattern: node?.compositionPattern,
|
|
229
|
+
subComponents,
|
|
230
|
+
children,
|
|
231
|
+
parent,
|
|
232
|
+
siblings,
|
|
233
|
+
blocks: this.blockIndex.get(component) ?? []
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** Get alternative components (deduplicated for bidirectional edges) */
|
|
237
|
+
alternatives(component) {
|
|
238
|
+
const seen = /* @__PURE__ */ new Set();
|
|
239
|
+
const alts = [];
|
|
240
|
+
for (const edge of this.outgoing.get(component) ?? []) {
|
|
241
|
+
if (edge.type === "alternative-to" && !seen.has(edge.target)) {
|
|
242
|
+
seen.add(edge.target);
|
|
243
|
+
alts.push({ component: edge.target, note: edge.note });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const edge of this.incoming.get(component) ?? []) {
|
|
247
|
+
if (edge.type === "alternative-to" && !seen.has(edge.source)) {
|
|
248
|
+
seen.add(edge.source);
|
|
249
|
+
alts.push({ component: edge.source, note: edge.note });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return alts;
|
|
253
|
+
}
|
|
254
|
+
/** Get blocks that use a component */
|
|
255
|
+
blocksUsing(component) {
|
|
256
|
+
return this.blockIndex.get(component) ?? [];
|
|
257
|
+
}
|
|
258
|
+
/** Extract an induced subgraph for a set of components */
|
|
259
|
+
subgraph(components) {
|
|
260
|
+
const componentSet = new Set(components);
|
|
261
|
+
const nodes = components.map((name) => this.nodes.get(name)).filter((n) => n !== void 0);
|
|
262
|
+
const edges = this.edges.filter(
|
|
263
|
+
(e) => componentSet.has(e.source) && componentSet.has(e.target)
|
|
264
|
+
);
|
|
265
|
+
return {
|
|
266
|
+
nodes,
|
|
267
|
+
edges,
|
|
268
|
+
health: computeHealthFromData(nodes, edges, this.blockIndex)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/** Return precomputed health metrics */
|
|
272
|
+
getHealth() {
|
|
273
|
+
return this.health;
|
|
274
|
+
}
|
|
275
|
+
/** Get a single node by name */
|
|
276
|
+
getNode(name) {
|
|
277
|
+
return this.nodes.get(name);
|
|
278
|
+
}
|
|
279
|
+
/** Check if a component exists in the graph */
|
|
280
|
+
hasNode(name) {
|
|
281
|
+
return this.nodes.has(name);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
function computeHealthFromData(nodes, edges, blockIndex) {
|
|
285
|
+
const nodeNames = new Set(nodes.map((n) => n.name));
|
|
286
|
+
const degreeMap = /* @__PURE__ */ new Map();
|
|
287
|
+
for (const name of nodeNames) {
|
|
288
|
+
degreeMap.set(name, 0);
|
|
289
|
+
}
|
|
290
|
+
for (const edge of edges) {
|
|
291
|
+
degreeMap.set(edge.source, (degreeMap.get(edge.source) ?? 0) + 1);
|
|
292
|
+
degreeMap.set(edge.target, (degreeMap.get(edge.target) ?? 0) + 1);
|
|
293
|
+
}
|
|
294
|
+
const orphans = [];
|
|
295
|
+
for (const [name, degree] of degreeMap) {
|
|
296
|
+
if (degree === 0) orphans.push(name);
|
|
297
|
+
}
|
|
298
|
+
const hubs = [...degreeMap.entries()].map(([name, degree]) => ({ name, degree })).sort((a, b) => b.degree - a.degree).slice(0, 10);
|
|
299
|
+
let inBlock = 0;
|
|
300
|
+
if (blockIndex) {
|
|
301
|
+
for (const name of nodeNames) {
|
|
302
|
+
if ((blockIndex.get(name) ?? []).length > 0) inBlock++;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const compositionCoverage = nodeNames.size > 0 ? Math.round(inBlock / nodeNames.size * 100) : 0;
|
|
306
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
307
|
+
for (const name of nodeNames) {
|
|
308
|
+
adjacency.set(name, /* @__PURE__ */ new Set());
|
|
309
|
+
}
|
|
310
|
+
for (const edge of edges) {
|
|
311
|
+
adjacency.get(edge.source)?.add(edge.target);
|
|
312
|
+
adjacency.get(edge.target)?.add(edge.source);
|
|
313
|
+
}
|
|
314
|
+
const visited = /* @__PURE__ */ new Set();
|
|
315
|
+
const connectedComponents = [];
|
|
316
|
+
for (const name of nodeNames) {
|
|
317
|
+
if (visited.has(name)) continue;
|
|
318
|
+
const island = [];
|
|
319
|
+
const queue = [name];
|
|
320
|
+
visited.add(name);
|
|
321
|
+
while (queue.length > 0) {
|
|
322
|
+
const current = queue.shift();
|
|
323
|
+
island.push(current);
|
|
324
|
+
for (const neighbor of adjacency.get(current) ?? []) {
|
|
325
|
+
if (!visited.has(neighbor)) {
|
|
326
|
+
visited.add(neighbor);
|
|
327
|
+
queue.push(neighbor);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
connectedComponents.push(island.sort());
|
|
332
|
+
}
|
|
333
|
+
connectedComponents.sort((a, b) => b.length - a.length);
|
|
334
|
+
const totalDegree = [...degreeMap.values()].reduce((sum, d) => sum + d, 0);
|
|
335
|
+
const averageDegree = nodeNames.size > 0 ? Math.round(totalDegree / nodeNames.size * 100) / 100 : 0;
|
|
336
|
+
return {
|
|
337
|
+
orphans: orphans.sort(),
|
|
338
|
+
hubs,
|
|
339
|
+
compositionCoverage,
|
|
340
|
+
connectedComponents,
|
|
341
|
+
averageDegree,
|
|
342
|
+
nodeCount: nodeNames.size,
|
|
343
|
+
edgeCount: edges.length
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/graph/serialization.ts
|
|
348
|
+
function serializeGraph(graph) {
|
|
349
|
+
return {
|
|
350
|
+
nodes: graph.nodes,
|
|
351
|
+
edges: graph.edges.map(serializeEdge),
|
|
352
|
+
health: graph.health
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
function deserializeGraph(serialized) {
|
|
356
|
+
return {
|
|
357
|
+
nodes: serialized.nodes,
|
|
358
|
+
edges: serialized.edges.map(deserializeEdge),
|
|
359
|
+
health: serialized.health
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function serializeEdge(edge) {
|
|
363
|
+
const result = {
|
|
364
|
+
s: edge.source,
|
|
365
|
+
t: edge.target,
|
|
366
|
+
ty: edge.type,
|
|
367
|
+
w: edge.weight,
|
|
368
|
+
p: edge.provenance
|
|
369
|
+
};
|
|
370
|
+
if (edge.note) result.no = edge.note;
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
function deserializeEdge(edge) {
|
|
374
|
+
const result = {
|
|
375
|
+
source: edge.s,
|
|
376
|
+
target: edge.t,
|
|
377
|
+
type: edge.ty,
|
|
378
|
+
weight: edge.w,
|
|
379
|
+
provenance: edge.p
|
|
380
|
+
};
|
|
381
|
+
if (edge.no) result.note = edge.no;
|
|
382
|
+
return result;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export {
|
|
386
|
+
GRAPH_EDGE_TYPES,
|
|
387
|
+
EDGE_TYPE_WEIGHTS,
|
|
388
|
+
ComponentGraphEngine,
|
|
389
|
+
computeHealthFromData,
|
|
390
|
+
serializeGraph,
|
|
391
|
+
deserializeGraph
|
|
392
|
+
};
|
|
393
|
+
//# sourceMappingURL=chunk-SH4KPIYH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/graph/types.ts","../src/graph/engine.ts","../src/graph/serialization.ts"],"sourcesContent":["/**\n * Component Graph data model — types for the design-system-semantic relationship graph.\n *\n * These types describe nodes (components), edges (relationships), and health\n * metrics that together give AI agents structural understanding of a design system.\n */\n\n// ---------------------------------------------------------------------------\n// Edge types — design-system-semantic\n// ---------------------------------------------------------------------------\n\nexport type GraphEdgeType =\n | 'imports' // A imports B in source code\n | 'hook-depends' // A calls useB() from B\n | 'renders' // A renders <B> in examples\n | 'composes' // A and B co-occur in a block\n | 'parent-of' // A should wrap B\n | 'alternative-to' // A can replace B\n | 'sibling-of'; // A and B at same level\n\nexport const GRAPH_EDGE_TYPES: readonly GraphEdgeType[] = [\n 'imports',\n 'hook-depends',\n 'renders',\n 'composes',\n 'parent-of',\n 'alternative-to',\n 'sibling-of',\n];\n\n/** Default weight for each edge type */\nexport const EDGE_TYPE_WEIGHTS: Record<GraphEdgeType, number> = {\n 'imports': 1.0,\n 'hook-depends': 0.75,\n 'renders': 0.5,\n 'composes': 0.5,\n 'parent-of': 1.0,\n 'alternative-to': 1.0,\n 'sibling-of': 0.75,\n};\n\n// ---------------------------------------------------------------------------\n// Nodes\n// ---------------------------------------------------------------------------\n\nexport interface ComponentNode {\n name: string;\n category: string;\n status: string;\n compositionPattern?: 'compound' | 'simple' | 'controlled' | 'wrapper';\n subComponents?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Edges\n// ---------------------------------------------------------------------------\n\nexport interface GraphEdge {\n source: string;\n target: string;\n type: GraphEdgeType;\n weight: number;\n note?: string;\n /** Where this edge was detected from */\n provenance: string;\n}\n\n// ---------------------------------------------------------------------------\n// Health metrics\n// ---------------------------------------------------------------------------\n\nexport interface GraphHealth {\n /** Components with zero edges */\n orphans: string[];\n /** Top components by total degree (in + out) */\n hubs: Array<{ name: string; degree: number }>;\n /** Percentage of components appearing in at least one block */\n compositionCoverage: number;\n /** Disconnected groups (BFS on undirected projection) */\n connectedComponents: string[][];\n /** Average degree across all nodes */\n averageDegree: number;\n /** Total node and edge counts */\n nodeCount: number;\n edgeCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Full graph\n// ---------------------------------------------------------------------------\n\nexport interface ComponentGraph {\n nodes: ComponentNode[];\n edges: GraphEdge[];\n health: GraphHealth;\n}\n\n// ---------------------------------------------------------------------------\n// Query results\n// ---------------------------------------------------------------------------\n\nexport interface ImpactResult {\n /** The component that was changed */\n component: string;\n /** Components affected at each depth level */\n affected: Array<{\n component: string;\n depth: number;\n path: string[];\n edgeType: GraphEdgeType;\n }>;\n /** Blocks that use the changed component or any affected component */\n affectedBlocks: string[];\n /** Total count of affected components */\n totalAffected: number;\n}\n\nexport interface PathResult {\n /** Whether a path exists */\n found: boolean;\n /** Nodes along the shortest path (including source and target) */\n path: string[];\n /** Edges along the shortest path */\n edges: GraphEdge[];\n}\n\nexport interface NeighborResult {\n /** The center component */\n component: string;\n /** All components reachable within maxHops */\n neighbors: Array<{\n component: string;\n hops: number;\n edgeType: GraphEdgeType;\n }>;\n}\n\nexport interface CompositionTree {\n /** The root component */\n component: string;\n /** Composition pattern */\n compositionPattern?: 'compound' | 'simple' | 'controlled' | 'wrapper';\n /** Direct sub-components */\n subComponents: string[];\n /** Components that declare this component as their parent */\n children: string[];\n /** Parent component (if this is a sub-component) */\n parent?: string;\n /** Sibling components at the same level */\n siblings: string[];\n /** Blocks that use this component */\n blocks: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Serialization (compact keys for JSON size)\n// ---------------------------------------------------------------------------\n\nexport interface SerializedEdge {\n /** source */\n s: string;\n /** target */\n t: string;\n /** type */\n ty: GraphEdgeType;\n /** weight */\n w: number;\n /** note (optional) */\n no?: string;\n /** provenance */\n p: string;\n}\n\nexport interface SerializedComponentGraph {\n nodes: ComponentNode[];\n edges: SerializedEdge[];\n health: GraphHealth;\n}\n","/**\n * ComponentGraphEngine — query engine for the design-system relationship graph.\n *\n * Provides BFS-based traversal, impact analysis, composition trees,\n * alternative discovery, and health metrics. Operates on an in-memory\n * adjacency list built from GraphEdge[].\n */\n\nimport type {\n ComponentNode,\n GraphEdge,\n GraphEdgeType,\n GraphHealth,\n ComponentGraph,\n ImpactResult,\n PathResult,\n NeighborResult,\n CompositionTree,\n} from './types.js';\n\nexport class ComponentGraphEngine {\n private nodes: Map<string, ComponentNode>;\n private outgoing: Map<string, GraphEdge[]>;\n private incoming: Map<string, GraphEdge[]>;\n private edges: GraphEdge[];\n private blockIndex: Map<string, string[]>;\n private health: GraphHealth;\n\n constructor(graph: ComponentGraph, blocks?: Record<string, { components: string[] }>) {\n this.nodes = new Map();\n this.outgoing = new Map();\n this.incoming = new Map();\n this.edges = graph.edges;\n this.health = graph.health;\n this.blockIndex = new Map();\n\n for (const node of graph.nodes) {\n this.nodes.set(node.name, node);\n this.outgoing.set(node.name, []);\n this.incoming.set(node.name, []);\n }\n\n for (const edge of graph.edges) {\n const out = this.outgoing.get(edge.source);\n if (out) out.push(edge);\n else this.outgoing.set(edge.source, [edge]);\n\n const inc = this.incoming.get(edge.target);\n if (inc) inc.push(edge);\n else this.incoming.set(edge.target, [edge]);\n }\n\n // Build block index: component → block names\n if (blocks) {\n for (const [blockName, block] of Object.entries(blocks)) {\n for (const comp of block.components) {\n const existing = this.blockIndex.get(comp);\n if (existing) existing.push(blockName);\n else this.blockIndex.set(comp, [blockName]);\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Core queries\n // -------------------------------------------------------------------------\n\n /** Get outgoing edges from a component, optionally filtered by edge type */\n dependencies(component: string, edgeTypes?: GraphEdgeType[]): GraphEdge[] {\n const edges = this.outgoing.get(component) ?? [];\n if (!edgeTypes || edgeTypes.length === 0) return edges;\n return edges.filter(e => edgeTypes.includes(e.type));\n }\n\n /** Get incoming edges to a component, optionally filtered by edge type */\n dependents(component: string, edgeTypes?: GraphEdgeType[]): GraphEdge[] {\n const edges = this.incoming.get(component) ?? [];\n if (!edgeTypes || edgeTypes.length === 0) return edges;\n return edges.filter(e => edgeTypes.includes(e.type));\n }\n\n /** BFS transitive closure — what's affected if this component changes */\n impact(component: string, maxDepth = 3): ImpactResult {\n const affected: ImpactResult['affected'] = [];\n const visited = new Set<string>([component]);\n const queue: Array<{ name: string; depth: number; path: string[] }> = [\n { name: component, depth: 0, path: [component] },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current.depth >= maxDepth) continue;\n\n // Components that depend on current (incoming edges = dependents)\n const deps = this.incoming.get(current.name) ?? [];\n for (const edge of deps) {\n if (visited.has(edge.source)) continue;\n visited.add(edge.source);\n const newPath = [...current.path, edge.source];\n affected.push({\n component: edge.source,\n depth: current.depth + 1,\n path: newPath,\n edgeType: edge.type,\n });\n queue.push({ name: edge.source, depth: current.depth + 1, path: newPath });\n }\n }\n\n // Find affected blocks\n const affectedComponents = new Set([component, ...affected.map(a => a.component)]);\n const affectedBlocks = new Set<string>();\n for (const comp of affectedComponents) {\n const blocks = this.blockIndex.get(comp);\n if (blocks) {\n for (const b of blocks) affectedBlocks.add(b);\n }\n }\n\n return {\n component,\n affected,\n affectedBlocks: [...affectedBlocks],\n totalAffected: affected.length,\n };\n }\n\n /** BFS shortest path between two components (undirected) */\n path(from: string, to: string): PathResult {\n if (from === to) {\n return { found: true, path: [from], edges: [] };\n }\n\n const visited = new Set<string>([from]);\n const queue: Array<{ name: string; path: string[]; edges: GraphEdge[] }> = [\n { name: from, path: [from], edges: [] },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n\n // Check both outgoing and incoming edges (undirected traversal)\n const allEdges = [\n ...(this.outgoing.get(current.name) ?? []),\n ...(this.incoming.get(current.name) ?? []),\n ];\n\n for (const edge of allEdges) {\n const neighbor = edge.source === current.name ? edge.target : edge.source;\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n\n const newPath = [...current.path, neighbor];\n const newEdges = [...current.edges, edge];\n\n if (neighbor === to) {\n return { found: true, path: newPath, edges: newEdges };\n }\n\n queue.push({ name: neighbor, path: newPath, edges: newEdges });\n }\n }\n\n return { found: false, path: [], edges: [] };\n }\n\n /** Connected components via BFS on undirected projection */\n islands(): string[][] {\n const visited = new Set<string>();\n const components: string[][] = [];\n\n for (const nodeName of this.nodes.keys()) {\n if (visited.has(nodeName)) continue;\n\n const island: string[] = [];\n const queue = [nodeName];\n visited.add(nodeName);\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n island.push(current);\n\n const allEdges = [\n ...(this.outgoing.get(current) ?? []),\n ...(this.incoming.get(current) ?? []),\n ];\n\n for (const edge of allEdges) {\n const neighbor = edge.source === current ? edge.target : edge.source;\n if (!visited.has(neighbor) && this.nodes.has(neighbor)) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n\n components.push(island.sort());\n }\n\n return components.sort((a, b) => b.length - a.length);\n }\n\n /** All components reachable within N hops (undirected) */\n neighbors(component: string, maxHops = 1): NeighborResult {\n const neighbors: NeighborResult['neighbors'] = [];\n const visited = new Set<string>([component]);\n const queue: Array<{ name: string; hops: number }> = [\n { name: component, hops: 0 },\n ];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current.hops >= maxHops) continue;\n\n const allEdges = [\n ...(this.outgoing.get(current.name) ?? []),\n ...(this.incoming.get(current.name) ?? []),\n ];\n\n for (const edge of allEdges) {\n const neighbor = edge.source === current.name ? edge.target : edge.source;\n if (visited.has(neighbor)) continue;\n visited.add(neighbor);\n\n neighbors.push({\n component: neighbor,\n hops: current.hops + 1,\n edgeType: edge.type,\n });\n\n queue.push({ name: neighbor, hops: current.hops + 1 });\n }\n }\n\n return { component, neighbors };\n }\n\n // -------------------------------------------------------------------------\n // Design-system queries\n // -------------------------------------------------------------------------\n\n /** Get the composition tree for a compound component */\n composition(component: string): CompositionTree {\n const node = this.nodes.get(component);\n\n // Find sub-components from node data\n const subComponents = node?.subComponents ?? [];\n\n // Find children from outgoing parent-of edges\n const parentEdges = (this.outgoing.get(component) ?? [])\n .filter(e => e.type === 'parent-of');\n const children = parentEdges.map(e => e.target);\n\n // Find parent (incoming parent-of edges)\n const childEdges = (this.incoming.get(component) ?? [])\n .filter(e => e.type === 'parent-of');\n const parent = childEdges.length > 0 ? childEdges[0].source : undefined;\n\n // Find siblings (other targets of the same parent)\n const siblings: string[] = [];\n if (parent) {\n const parentOut = (this.outgoing.get(parent) ?? [])\n .filter(e => e.type === 'parent-of');\n for (const edge of parentOut) {\n if (edge.target !== component) {\n siblings.push(edge.target);\n }\n }\n }\n\n // Also check sibling-of edges\n const siblingEdges = [\n ...(this.outgoing.get(component) ?? []).filter(e => e.type === 'sibling-of'),\n ...(this.incoming.get(component) ?? []).filter(e => e.type === 'sibling-of'),\n ];\n for (const edge of siblingEdges) {\n const sib = edge.source === component ? edge.target : edge.source;\n if (!siblings.includes(sib)) siblings.push(sib);\n }\n\n return {\n component,\n compositionPattern: node?.compositionPattern,\n subComponents,\n children,\n parent,\n siblings,\n blocks: this.blockIndex.get(component) ?? [],\n };\n }\n\n /** Get alternative components (deduplicated for bidirectional edges) */\n alternatives(component: string): Array<{ component: string; note?: string }> {\n const seen = new Set<string>();\n const alts: Array<{ component: string; note?: string }> = [];\n\n // Outgoing alternative-to edges\n for (const edge of this.outgoing.get(component) ?? []) {\n if (edge.type === 'alternative-to' && !seen.has(edge.target)) {\n seen.add(edge.target);\n alts.push({ component: edge.target, note: edge.note });\n }\n }\n\n // Incoming alternative-to edges (skip if already seen from outgoing)\n for (const edge of this.incoming.get(component) ?? []) {\n if (edge.type === 'alternative-to' && !seen.has(edge.source)) {\n seen.add(edge.source);\n alts.push({ component: edge.source, note: edge.note });\n }\n }\n\n return alts;\n }\n\n /** Get blocks that use a component */\n blocksUsing(component: string): string[] {\n return this.blockIndex.get(component) ?? [];\n }\n\n /** Extract an induced subgraph for a set of components */\n subgraph(components: string[]): ComponentGraph {\n const componentSet = new Set(components);\n const nodes = components\n .map(name => this.nodes.get(name))\n .filter((n): n is ComponentNode => n !== undefined);\n\n const edges = this.edges.filter(\n e => componentSet.has(e.source) && componentSet.has(e.target)\n );\n\n return {\n nodes,\n edges,\n health: computeHealthFromData(nodes, edges, this.blockIndex),\n };\n }\n\n /** Return precomputed health metrics */\n getHealth(): GraphHealth {\n return this.health;\n }\n\n /** Get a single node by name */\n getNode(name: string): ComponentNode | undefined {\n return this.nodes.get(name);\n }\n\n /** Check if a component exists in the graph */\n hasNode(name: string): boolean {\n return this.nodes.has(name);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Health computation helper (also used by graph-extractor)\n// ---------------------------------------------------------------------------\n\nexport function computeHealthFromData(\n nodes: ComponentNode[],\n edges: GraphEdge[],\n blockIndex?: Map<string, string[]>\n): GraphHealth {\n const nodeNames = new Set(nodes.map(n => n.name));\n const degreeMap = new Map<string, number>();\n\n for (const name of nodeNames) {\n degreeMap.set(name, 0);\n }\n\n for (const edge of edges) {\n degreeMap.set(edge.source, (degreeMap.get(edge.source) ?? 0) + 1);\n degreeMap.set(edge.target, (degreeMap.get(edge.target) ?? 0) + 1);\n }\n\n // Orphans: zero degree\n const orphans: string[] = [];\n for (const [name, degree] of degreeMap) {\n if (degree === 0) orphans.push(name);\n }\n\n // Hubs: top 10 by degree\n const hubs = [...degreeMap.entries()]\n .map(([name, degree]) => ({ name, degree }))\n .sort((a, b) => b.degree - a.degree)\n .slice(0, 10);\n\n // Composition coverage\n let inBlock = 0;\n if (blockIndex) {\n for (const name of nodeNames) {\n if ((blockIndex.get(name) ?? []).length > 0) inBlock++;\n }\n }\n const compositionCoverage = nodeNames.size > 0\n ? Math.round((inBlock / nodeNames.size) * 100)\n : 0;\n\n // Connected components (BFS on undirected projection)\n const adjacency = new Map<string, Set<string>>();\n for (const name of nodeNames) {\n adjacency.set(name, new Set());\n }\n for (const edge of edges) {\n adjacency.get(edge.source)?.add(edge.target);\n adjacency.get(edge.target)?.add(edge.source);\n }\n\n const visited = new Set<string>();\n const connectedComponents: string[][] = [];\n for (const name of nodeNames) {\n if (visited.has(name)) continue;\n const island: string[] = [];\n const queue = [name];\n visited.add(name);\n while (queue.length > 0) {\n const current = queue.shift()!;\n island.push(current);\n for (const neighbor of adjacency.get(current) ?? []) {\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n queue.push(neighbor);\n }\n }\n }\n connectedComponents.push(island.sort());\n }\n connectedComponents.sort((a, b) => b.length - a.length);\n\n // Average degree\n const totalDegree = [...degreeMap.values()].reduce((sum, d) => sum + d, 0);\n const averageDegree = nodeNames.size > 0\n ? Math.round((totalDegree / nodeNames.size) * 100) / 100\n : 0;\n\n return {\n orphans: orphans.sort(),\n hubs,\n compositionCoverage,\n connectedComponents,\n averageDegree,\n nodeCount: nodeNames.size,\n edgeCount: edges.length,\n };\n}\n","/**\n * Graph serialization — compact JSON format for fragments.json embedding.\n *\n * Edges use short keys (s, t, ty, w, no, p) to reduce JSON size.\n * Round-trips through serialize → deserialize preserve all data.\n */\n\nimport type {\n ComponentGraph,\n GraphEdge,\n SerializedComponentGraph,\n SerializedEdge,\n} from './types.js';\n\n/** Serialize a ComponentGraph to compact JSON format */\nexport function serializeGraph(graph: ComponentGraph): SerializedComponentGraph {\n return {\n nodes: graph.nodes,\n edges: graph.edges.map(serializeEdge),\n health: graph.health,\n };\n}\n\n/** Deserialize a compact JSON graph back to full ComponentGraph */\nexport function deserializeGraph(serialized: SerializedComponentGraph): ComponentGraph {\n return {\n nodes: serialized.nodes,\n edges: serialized.edges.map(deserializeEdge),\n health: serialized.health,\n };\n}\n\nfunction serializeEdge(edge: GraphEdge): SerializedEdge {\n const result: SerializedEdge = {\n s: edge.source,\n t: edge.target,\n ty: edge.type,\n w: edge.weight,\n p: edge.provenance,\n };\n if (edge.note) result.no = edge.note;\n return result;\n}\n\nfunction deserializeEdge(edge: SerializedEdge): GraphEdge {\n const result: GraphEdge = {\n source: edge.s,\n target: edge.t,\n type: edge.ty,\n weight: edge.w,\n provenance: edge.p,\n };\n if (edge.no) result.note = edge.no;\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/severity.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var severitySchema = z.enum(["critical", "serious", "moderate", "minor"]);
|
|
4
|
+
var severityLevelSchema = z.enum(["error", "warn"]);
|
|
5
|
+
var legacySeverityLevelSchema = z.enum(["error", "warn", "info", "warning"]);
|
|
6
|
+
var SEVERITIES = severitySchema.options;
|
|
7
|
+
var SEVERITY_WEIGHTS = {
|
|
8
|
+
critical: 10,
|
|
9
|
+
serious: 5,
|
|
10
|
+
moderate: 2,
|
|
11
|
+
minor: 1
|
|
12
|
+
};
|
|
13
|
+
var SEVERITY_RANK = {
|
|
14
|
+
critical: 4,
|
|
15
|
+
serious: 3,
|
|
16
|
+
moderate: 2,
|
|
17
|
+
minor: 1
|
|
18
|
+
};
|
|
19
|
+
function severityLevel(severity) {
|
|
20
|
+
return severity === "critical" || severity === "serious" ? "error" : "warn";
|
|
21
|
+
}
|
|
22
|
+
function severityFromLevel(level) {
|
|
23
|
+
switch (level) {
|
|
24
|
+
case "error":
|
|
25
|
+
return "serious";
|
|
26
|
+
case "warn":
|
|
27
|
+
case "warning":
|
|
28
|
+
return "moderate";
|
|
29
|
+
case "info":
|
|
30
|
+
return "minor";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function compareSeverity(a, b) {
|
|
34
|
+
return SEVERITY_RANK[b] - SEVERITY_RANK[a];
|
|
35
|
+
}
|
|
36
|
+
function maxSeverity(a, b) {
|
|
37
|
+
return SEVERITY_RANK[a] >= SEVERITY_RANK[b] ? a : b;
|
|
38
|
+
}
|
|
39
|
+
function sortBySeverity(items, getSeverity) {
|
|
40
|
+
return items.slice().sort((a, b) => compareSeverity(getSeverity(a), getSeverity(b)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
severitySchema,
|
|
45
|
+
severityLevelSchema,
|
|
46
|
+
legacySeverityLevelSchema,
|
|
47
|
+
SEVERITIES,
|
|
48
|
+
SEVERITY_WEIGHTS,
|
|
49
|
+
SEVERITY_RANK,
|
|
50
|
+
severityLevel,
|
|
51
|
+
severityFromLevel,
|
|
52
|
+
compareSeverity,
|
|
53
|
+
maxSeverity,
|
|
54
|
+
sortBySeverity
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=chunk-V4VQB57N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/severity.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const severitySchema = z.enum([\"critical\", \"serious\", \"moderate\", \"minor\"]);\nexport type Severity = z.infer<typeof severitySchema>;\n\nexport const severityLevelSchema = z.enum([\"error\", \"warn\"]);\nexport type SeverityLevel = z.infer<typeof severityLevelSchema>;\n\nexport const legacySeverityLevelSchema = z.enum([\"error\", \"warn\", \"info\", \"warning\"]);\nexport type LegacySeverityLevel = z.infer<typeof legacySeverityLevelSchema>;\n\nexport const SEVERITIES = severitySchema.options;\n\nexport const SEVERITY_WEIGHTS: Record<Severity, number> = {\n critical: 10,\n serious: 5,\n moderate: 2,\n minor: 1,\n};\n\nexport const SEVERITY_RANK: Record<Severity, number> = {\n critical: 4,\n serious: 3,\n moderate: 2,\n minor: 1,\n};\n\nexport function severityLevel(severity: Severity): SeverityLevel {\n return severity === \"critical\" || severity === \"serious\" ? \"error\" : \"warn\";\n}\n\nexport function severityFromLevel(level: LegacySeverityLevel): Severity {\n switch (level) {\n case \"error\":\n return \"serious\";\n case \"warn\":\n case \"warning\":\n return \"moderate\";\n case \"info\":\n return \"minor\";\n }\n}\n\nexport function compareSeverity(a: Severity, b: Severity): number {\n return SEVERITY_RANK[b] - SEVERITY_RANK[a];\n}\n\nexport function maxSeverity(a: Severity, b: Severity): Severity {\n return SEVERITY_RANK[a] >= SEVERITY_RANK[b] ? a : b;\n}\n\nexport function sortBySeverity<T>(items: readonly T[], getSeverity: (item: T) => Severity): T[] {\n return items.slice().sort((a, b) => compareSeverity(getSeverity(a), getSeverity(b)));\n}\n"],"mappings":";AAAA,SAAS,SAAS;AAEX,IAAM,iBAAiB,EAAE,KAAK,CAAC,YAAY,WAAW,YAAY,OAAO,CAAC;AAG1E,IAAM,sBAAsB,EAAE,KAAK,CAAC,SAAS,MAAM,CAAC;AAGpD,IAAM,4BAA4B,EAAE,KAAK,CAAC,SAAS,QAAQ,QAAQ,SAAS,CAAC;AAG7E,IAAM,aAAa,eAAe;AAElC,IAAM,mBAA6C;AAAA,EACxD,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AACT;AAEO,IAAM,gBAA0C;AAAA,EACrD,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AACT;AAEO,SAAS,cAAc,UAAmC;AAC/D,SAAO,aAAa,cAAc,aAAa,YAAY,UAAU;AACvE;AAEO,SAAS,kBAAkB,OAAsC;AACtE,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAEO,SAAS,gBAAgB,GAAa,GAAqB;AAChE,SAAO,cAAc,CAAC,IAAI,cAAc,CAAC;AAC3C;AAEO,SAAS,YAAY,GAAa,GAAuB;AAC9D,SAAO,cAAc,CAAC,KAAK,cAAc,CAAC,IAAI,IAAI;AACpD;AAEO,SAAS,eAAkB,OAAqB,aAAyC;AAC9F,SAAO,MAAM,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,gBAAgB,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC;AACrF;","names":[]}
|