@dreamtree-org/graphify 1.4.0 → 1.6.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.
@@ -2,12 +2,12 @@ import {
2
2
  LocalFileGraphStore,
3
3
  affectedBy,
4
4
  testsForNode
5
- } from "./chunk-7LTO76UD.js";
5
+ } from "./chunk-OHN5UO6W.js";
6
6
  import {
7
7
  escapeHtml,
8
8
  sanitizeLabel,
9
9
  validateGraphPath
10
- } from "./chunk-6JLEILYF.js";
10
+ } from "./chunk-N3VOEXK3.js";
11
11
 
12
12
  // src/detect.ts
13
13
  import * as fs from "fs";
@@ -1955,13 +1955,15 @@ var RelationSchema = z.enum([
1955
1955
  "references",
1956
1956
  "contains",
1957
1957
  "method",
1958
- "re_exports"
1958
+ "re_exports",
1959
+ "follows"
1959
1960
  ]);
1960
1961
  var GraphNodeSchema = z.object({
1961
1962
  id: z.string().min(1, "node id must be non-empty"),
1962
1963
  label: z.string(),
1963
1964
  sourceFile: z.string(),
1964
- sourceLocation: z.string()
1965
+ sourceLocation: z.string(),
1966
+ kind: z.string().optional()
1965
1967
  });
1966
1968
  var GraphEdgeSchema = z.object({
1967
1969
  source: z.string().min(1, "edge source must be non-empty"),
@@ -2010,16 +2012,27 @@ function buildGraph(extractions) {
2010
2012
  const validated = extractions.map(
2011
2013
  (extraction, index) => validateExtraction(extraction, `extraction #${index}`)
2012
2014
  );
2015
+ mergeExtractionsInto(graph, validated);
2016
+ return graph;
2017
+ }
2018
+ function mergeExtractionsInto(graph, validated) {
2013
2019
  const allNodes = validated.flatMap((extraction) => extraction.nodes);
2014
2020
  const allEdges = validated.flatMap((extraction) => extraction.edges);
2015
2021
  const sortedNodes = [...allNodes].sort((a, b) => a.id.localeCompare(b.id));
2016
2022
  for (const node of sortedNodes) {
2017
- if (graph.hasNode(node.id)) continue;
2018
- graph.addNode(node.id, {
2023
+ const attributes = {
2019
2024
  label: node.label,
2020
2025
  sourceFile: node.sourceFile,
2021
- sourceLocation: node.sourceLocation
2022
- });
2026
+ sourceLocation: node.sourceLocation,
2027
+ ...node.kind !== void 0 ? { kind: node.kind } : {}
2028
+ };
2029
+ if (graph.hasNode(node.id)) {
2030
+ if (graph.getNodeAttribute(node.id, "sourceFile") === "<unknown>") {
2031
+ graph.mergeNodeAttributes(node.id, attributes);
2032
+ }
2033
+ continue;
2034
+ }
2035
+ graph.addNode(node.id, attributes);
2023
2036
  }
2024
2037
  const sortedEdges = [...allEdges].sort(
2025
2038
  (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || a.relation.localeCompare(b.relation)
@@ -2038,6 +2051,98 @@ function buildGraph(extractions) {
2038
2051
  confidence: edge.confidence
2039
2052
  });
2040
2053
  }
2054
+ }
2055
+
2056
+ // src/cluster.ts
2057
+ import { createHash } from "crypto";
2058
+ import Graph2 from "graphology";
2059
+ import leiden from "@aflsolutions/graphology-communities-leiden";
2060
+ import louvain from "graphology-communities-louvain";
2061
+ var FIXED_SEED = 1592594996;
2062
+ function mulberry32(seed) {
2063
+ let a = seed >>> 0;
2064
+ return function next() {
2065
+ a = a + 1831565813 | 0;
2066
+ let t = Math.imul(a ^ a >>> 15, 1 | a);
2067
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2068
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
2069
+ };
2070
+ }
2071
+ function sortedWorkingCopy(graph) {
2072
+ const copy = new Graph2({ type: graph.type, multi: graph.multi, allowSelfLoops: graph.allowSelfLoops });
2073
+ const nodeIds = graph.nodes().sort((a, b) => a.localeCompare(b));
2074
+ for (const id of nodeIds) {
2075
+ copy.addNode(id, { ...graph.getNodeAttributes(id) });
2076
+ }
2077
+ const edgeEntries = [];
2078
+ graph.forEachEdge((edge, attributes, source, target) => {
2079
+ edgeEntries.push({ key: edge, source, target, attributes });
2080
+ });
2081
+ edgeEntries.sort(
2082
+ (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || String(a.attributes.relation).localeCompare(String(b.attributes.relation))
2083
+ );
2084
+ for (const entry of edgeEntries) {
2085
+ copy.addEdgeWithKey(entry.key, entry.source, entry.target, { ...entry.attributes });
2086
+ }
2087
+ return copy;
2088
+ }
2089
+ function toUndirectedCopy(graph) {
2090
+ const copy = new Graph2({ type: "undirected", multi: true, allowSelfLoops: graph.allowSelfLoops });
2091
+ graph.forEachNode((nodeId, attributes) => copy.addNode(nodeId, { ...attributes }));
2092
+ graph.forEachEdge((edgeKey, attributes, source, target) => {
2093
+ copy.addEdgeWithKey(edgeKey, source, target, { ...attributes });
2094
+ });
2095
+ return copy;
2096
+ }
2097
+ function runAlgorithm(working, options) {
2098
+ const rng = mulberry32(FIXED_SEED);
2099
+ if (options.algorithm === "leiden") {
2100
+ const undirected = toUndirectedCopy(working);
2101
+ leiden.assign(undirected, { rng, maxIterations: options.maxIterations ?? 0 });
2102
+ undirected.forEachNode((nodeId, attributes) => {
2103
+ working.setNodeAttribute(nodeId, "community", attributes.community);
2104
+ });
2105
+ return;
2106
+ }
2107
+ louvain.assign(working, { rng });
2108
+ }
2109
+ function cluster(graph, options = {}) {
2110
+ if (graph.order === 0) return graph;
2111
+ const working = sortedWorkingCopy(graph);
2112
+ runAlgorithm(working, options);
2113
+ const hubs = /* @__PURE__ */ new Map();
2114
+ working.forEachNode((nodeId) => {
2115
+ const community = working.getNodeAttribute(nodeId, "community");
2116
+ const degree = working.degree(nodeId);
2117
+ const current = hubs.get(community);
2118
+ if (!current || degree > current.degree) {
2119
+ hubs.set(community, { id: nodeId, degree });
2120
+ }
2121
+ });
2122
+ const membersByCommunity = /* @__PURE__ */ new Map();
2123
+ working.forEachNode((nodeId) => {
2124
+ const community = working.getNodeAttribute(nodeId, "community");
2125
+ const list = membersByCommunity.get(community);
2126
+ if (list) list.push(nodeId);
2127
+ else membersByCommunity.set(community, [nodeId]);
2128
+ });
2129
+ const labelByCommunity = /* @__PURE__ */ new Map();
2130
+ const hashByCommunity = /* @__PURE__ */ new Map();
2131
+ for (const [community, members] of membersByCommunity) {
2132
+ members.sort((a, b) => a.localeCompare(b));
2133
+ hashByCommunity.set(community, createHash("sha256").update(members.join("\n")).digest("hex"));
2134
+ const hub = hubs.get(community);
2135
+ const hubLabel = hub ? working.getNodeAttribute(hub.id, "label") : void 0;
2136
+ labelByCommunity.set(community, hubLabel && hubLabel.length > 0 ? hubLabel : `community-${community}`);
2137
+ }
2138
+ working.forEachNode((nodeId) => {
2139
+ const community = working.getNodeAttribute(nodeId, "community");
2140
+ graph.mergeNodeAttributes(nodeId, {
2141
+ community,
2142
+ communityLabel: labelByCommunity.get(community),
2143
+ communityHash: hashByCommunity.get(community)
2144
+ });
2145
+ });
2041
2146
  return graph;
2042
2147
  }
2043
2148
 
@@ -2235,99 +2340,6 @@ function resolveCrossFileReferences(extractions, options = {}) {
2235
2340
  return { resolvedEndpoints, droppedPlaceholders };
2236
2341
  }
2237
2342
 
2238
- // src/cluster.ts
2239
- import { createHash } from "crypto";
2240
- import Graph2 from "graphology";
2241
- import leiden from "@aflsolutions/graphology-communities-leiden";
2242
- import louvain from "graphology-communities-louvain";
2243
- var FIXED_SEED = 1592594996;
2244
- function mulberry32(seed) {
2245
- let a = seed >>> 0;
2246
- return function next() {
2247
- a = a + 1831565813 | 0;
2248
- let t = Math.imul(a ^ a >>> 15, 1 | a);
2249
- t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
2250
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
2251
- };
2252
- }
2253
- function sortedWorkingCopy(graph) {
2254
- const copy = new Graph2({ type: graph.type, multi: graph.multi, allowSelfLoops: graph.allowSelfLoops });
2255
- const nodeIds = graph.nodes().sort((a, b) => a.localeCompare(b));
2256
- for (const id of nodeIds) {
2257
- copy.addNode(id, { ...graph.getNodeAttributes(id) });
2258
- }
2259
- const edgeEntries = [];
2260
- graph.forEachEdge((edge, attributes, source, target) => {
2261
- edgeEntries.push({ key: edge, source, target, attributes });
2262
- });
2263
- edgeEntries.sort(
2264
- (a, b) => a.source.localeCompare(b.source) || a.target.localeCompare(b.target) || String(a.attributes.relation).localeCompare(String(b.attributes.relation))
2265
- );
2266
- for (const entry of edgeEntries) {
2267
- copy.addEdgeWithKey(entry.key, entry.source, entry.target, { ...entry.attributes });
2268
- }
2269
- return copy;
2270
- }
2271
- function toUndirectedCopy(graph) {
2272
- const copy = new Graph2({ type: "undirected", multi: true, allowSelfLoops: graph.allowSelfLoops });
2273
- graph.forEachNode((nodeId, attributes) => copy.addNode(nodeId, { ...attributes }));
2274
- graph.forEachEdge((edgeKey, attributes, source, target) => {
2275
- copy.addEdgeWithKey(edgeKey, source, target, { ...attributes });
2276
- });
2277
- return copy;
2278
- }
2279
- function runAlgorithm(working, options) {
2280
- const rng = mulberry32(FIXED_SEED);
2281
- if (options.algorithm === "leiden") {
2282
- const undirected = toUndirectedCopy(working);
2283
- leiden.assign(undirected, { rng, maxIterations: options.maxIterations ?? 0 });
2284
- undirected.forEachNode((nodeId, attributes) => {
2285
- working.setNodeAttribute(nodeId, "community", attributes.community);
2286
- });
2287
- return;
2288
- }
2289
- louvain.assign(working, { rng });
2290
- }
2291
- function cluster(graph, options = {}) {
2292
- if (graph.order === 0) return graph;
2293
- const working = sortedWorkingCopy(graph);
2294
- runAlgorithm(working, options);
2295
- const hubs = /* @__PURE__ */ new Map();
2296
- working.forEachNode((nodeId) => {
2297
- const community = working.getNodeAttribute(nodeId, "community");
2298
- const degree = working.degree(nodeId);
2299
- const current = hubs.get(community);
2300
- if (!current || degree > current.degree) {
2301
- hubs.set(community, { id: nodeId, degree });
2302
- }
2303
- });
2304
- const membersByCommunity = /* @__PURE__ */ new Map();
2305
- working.forEachNode((nodeId) => {
2306
- const community = working.getNodeAttribute(nodeId, "community");
2307
- const list = membersByCommunity.get(community);
2308
- if (list) list.push(nodeId);
2309
- else membersByCommunity.set(community, [nodeId]);
2310
- });
2311
- const labelByCommunity = /* @__PURE__ */ new Map();
2312
- const hashByCommunity = /* @__PURE__ */ new Map();
2313
- for (const [community, members] of membersByCommunity) {
2314
- members.sort((a, b) => a.localeCompare(b));
2315
- hashByCommunity.set(community, createHash("sha256").update(members.join("\n")).digest("hex"));
2316
- const hub = hubs.get(community);
2317
- const hubLabel = hub ? working.getNodeAttribute(hub.id, "label") : void 0;
2318
- labelByCommunity.set(community, hubLabel && hubLabel.length > 0 ? hubLabel : `community-${community}`);
2319
- }
2320
- working.forEachNode((nodeId) => {
2321
- const community = working.getNodeAttribute(nodeId, "community");
2322
- graph.mergeNodeAttributes(nodeId, {
2323
- community,
2324
- communityLabel: labelByCommunity.get(community),
2325
- communityHash: hashByCommunity.get(community)
2326
- });
2327
- });
2328
- return graph;
2329
- }
2330
-
2331
2343
  // src/analyze.ts
2332
2344
  var ABSOLUTE_DEGREE_FLOOR = 10;
2333
2345
  var MEAN_DEGREE_MULTIPLIER = 3;
@@ -3106,8 +3118,9 @@ export {
3106
3118
  ExtractionValidationError,
3107
3119
  validateExtraction,
3108
3120
  buildGraph,
3109
- resolveCrossFileReferences,
3121
+ mergeExtractionsInto,
3110
3122
  cluster,
3123
+ resolveCrossFileReferences,
3111
3124
  analyze,
3112
3125
  renderReport,
3113
3126
  exportGraph,
@@ -3125,4 +3138,4 @@ export {
3125
3138
  validateRules,
3126
3139
  checkRules
3127
3140
  };
3128
- //# sourceMappingURL=chunk-QEB7A5KB.js.map
3141
+ //# sourceMappingURL=chunk-5Q4BCTMF.js.map