ts-knowledge-graph 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +26 -8
  2. package/contribs/{web_visualisation → webview}/README.md +7 -7
  3. package/contribs/webview/web/css/style.css +310 -0
  4. package/contribs/{web_visualisation → webview}/web/index.html +40 -5
  5. package/contribs/{web_visualisation → webview}/web/js/app.js +378 -39
  6. package/contribs/{web_visualisation/web/data → webview/web/js_autogenerated}/kind_descriptions.js +2 -1
  7. package/contribs/{web_visualisation → webview}/web/types/app_globals.d.ts +11 -3
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +6 -2
  10. package/dist/cli.js.map +1 -1
  11. package/dist/cluster/cluster_weights.d.ts +20 -0
  12. package/dist/cluster/cluster_weights.d.ts.map +1 -0
  13. package/dist/cluster/cluster_weights.js +32 -0
  14. package/dist/cluster/cluster_weights.js.map +1 -0
  15. package/dist/cluster/community_detector.d.ts +61 -0
  16. package/dist/cluster/community_detector.d.ts.map +1 -0
  17. package/dist/cluster/community_detector.js +120 -0
  18. package/dist/cluster/community_detector.js.map +1 -0
  19. package/dist/cluster/community_labeler.d.ts +84 -0
  20. package/dist/cluster/community_labeler.d.ts.map +1 -0
  21. package/dist/cluster/community_labeler.js +194 -0
  22. package/dist/cluster/community_labeler.js.map +1 -0
  23. package/dist/cluster/graph_clusterer.d.ts +47 -0
  24. package/dist/cluster/graph_clusterer.d.ts.map +1 -0
  25. package/dist/cluster/graph_clusterer.js +126 -0
  26. package/dist/cluster/graph_clusterer.js.map +1 -0
  27. package/dist/commands/benchmark_command.d.ts.map +1 -1
  28. package/dist/commands/benchmark_command.js +13 -10
  29. package/dist/commands/benchmark_command.js.map +1 -1
  30. package/dist/commands/blast_radius_command.d.ts.map +1 -1
  31. package/dist/commands/blast_radius_command.js +6 -5
  32. package/dist/commands/blast_radius_command.js.map +1 -1
  33. package/dist/commands/cluster_command.d.ts +7 -0
  34. package/dist/commands/cluster_command.d.ts.map +1 -0
  35. package/dist/commands/cluster_command.js +55 -0
  36. package/dist/commands/cluster_command.js.map +1 -0
  37. package/dist/commands/command_helpers.d.ts +9 -4
  38. package/dist/commands/command_helpers.d.ts.map +1 -1
  39. package/dist/commands/command_helpers.js +13 -8
  40. package/dist/commands/command_helpers.js.map +1 -1
  41. package/dist/commands/cost_command.d.ts.map +1 -1
  42. package/dist/commands/cost_command.js +25 -8
  43. package/dist/commands/cost_command.js.map +1 -1
  44. package/dist/commands/enrich_command.d.ts.map +1 -1
  45. package/dist/commands/enrich_command.js +7 -5
  46. package/dist/commands/enrich_command.js.map +1 -1
  47. package/dist/commands/extract_command.d.ts.map +1 -1
  48. package/dist/commands/extract_command.js +12 -6
  49. package/dist/commands/extract_command.js.map +1 -1
  50. package/dist/commands/hotspots_command.d.ts.map +1 -1
  51. package/dist/commands/hotspots_command.js +6 -5
  52. package/dist/commands/hotspots_command.js.map +1 -1
  53. package/dist/commands/install_command.d.ts +15 -5
  54. package/dist/commands/install_command.d.ts.map +1 -1
  55. package/dist/commands/install_command.js +61 -23
  56. package/dist/commands/install_command.js.map +1 -1
  57. package/dist/commands/load_command.d.ts.map +1 -1
  58. package/dist/commands/load_command.js +18 -13
  59. package/dist/commands/load_command.js.map +1 -1
  60. package/dist/commands/neighbors_command.d.ts.map +1 -1
  61. package/dist/commands/neighbors_command.js +6 -5
  62. package/dist/commands/neighbors_command.js.map +1 -1
  63. package/dist/commands/references_command.d.ts.map +1 -1
  64. package/dist/commands/references_command.js +6 -5
  65. package/dist/commands/references_command.js.map +1 -1
  66. package/dist/commands/report_command.d.ts +16 -0
  67. package/dist/commands/report_command.d.ts.map +1 -0
  68. package/dist/commands/report_command.js +115 -0
  69. package/dist/commands/report_command.js.map +1 -0
  70. package/dist/commands/webview_command.d.ts +36 -0
  71. package/dist/commands/webview_command.d.ts.map +1 -0
  72. package/dist/commands/webview_command.js +186 -0
  73. package/dist/commands/webview_command.js.map +1 -0
  74. package/dist/enrich/cpu_profile.d.ts +33 -0
  75. package/dist/enrich/cpu_profile.d.ts.map +1 -1
  76. package/dist/enrich/cpu_profile.js +88 -0
  77. package/dist/enrich/cpu_profile.js.map +1 -1
  78. package/dist/enrich/runtime_enricher.d.ts +8 -0
  79. package/dist/enrich/runtime_enricher.d.ts.map +1 -1
  80. package/dist/enrich/runtime_enricher.js +18 -0
  81. package/dist/enrich/runtime_enricher.js.map +1 -1
  82. package/dist/enrich/runtime_join.d.ts +25 -1
  83. package/dist/enrich/runtime_join.d.ts.map +1 -1
  84. package/dist/enrich/runtime_join.js +43 -0
  85. package/dist/enrich/runtime_join.js.map +1 -1
  86. package/dist/extract/git_source.d.ts +23 -0
  87. package/dist/extract/git_source.d.ts.map +1 -0
  88. package/dist/extract/git_source.js +75 -0
  89. package/dist/extract/git_source.js.map +1 -0
  90. package/dist/query/graph_query.d.ts +36 -1
  91. package/dist/query/graph_query.d.ts.map +1 -1
  92. package/dist/query/graph_query.js +69 -6
  93. package/dist/query/graph_query.js.map +1 -1
  94. package/dist/report/graph_report.d.ts +51 -0
  95. package/dist/report/graph_report.d.ts.map +1 -0
  96. package/dist/report/graph_report.js +312 -0
  97. package/dist/report/graph_report.js.map +1 -0
  98. package/dist/report/pdf_renderer.d.ts +22 -0
  99. package/dist/report/pdf_renderer.d.ts.map +1 -0
  100. package/dist/report/pdf_renderer.js +54 -0
  101. package/dist/report/pdf_renderer.js.map +1 -0
  102. package/dist/report/report_data.d.ts +128 -0
  103. package/dist/report/report_data.d.ts.map +1 -0
  104. package/dist/report/report_data.js +191 -0
  105. package/dist/report/report_data.js.map +1 -0
  106. package/dist/schema/edge.d.ts +5 -5
  107. package/dist/schema/edge.d.ts.map +1 -1
  108. package/dist/schema/edge.js +3 -0
  109. package/dist/schema/edge.js.map +1 -1
  110. package/dist/schema/source_manifest.d.ts +30 -0
  111. package/dist/schema/source_manifest.d.ts.map +1 -0
  112. package/dist/schema/source_manifest.js +21 -0
  113. package/dist/schema/source_manifest.js.map +1 -0
  114. package/dist/store/jsonl_reader.d.ts +4 -0
  115. package/dist/store/jsonl_reader.d.ts.map +1 -1
  116. package/dist/store/jsonl_reader.js +13 -1
  117. package/dist/store/jsonl_reader.js.map +1 -1
  118. package/dist/store/jsonl_store.d.ts +2 -1
  119. package/dist/store/jsonl_store.d.ts.map +1 -1
  120. package/dist/store/jsonl_store.js +4 -1
  121. package/dist/store/jsonl_store.js.map +1 -1
  122. package/dist/store/kuzu_store.d.ts +13 -0
  123. package/dist/store/kuzu_store.d.ts.map +1 -1
  124. package/dist/store/kuzu_store.js +29 -0
  125. package/dist/store/kuzu_store.js.map +1 -1
  126. package/dist/store/output_folder.d.ts +43 -0
  127. package/dist/store/output_folder.d.ts.map +1 -0
  128. package/dist/store/output_folder.js +61 -0
  129. package/dist/store/output_folder.js.map +1 -0
  130. package/dotclaude_folder/commands/code-graph-interview.md +123 -0
  131. package/dotclaude_folder/commands/code-graph-optimize.md +65 -0
  132. package/dotclaude_folder/skills/code-graph-query/SKILL.md +4 -4
  133. package/package.json +72 -62
  134. package/contribs/web_visualisation/web/css/style.css +0 -219
  135. package/contribs/web_visualisation/web/tsconfig.json +0 -18
  136. /package/contribs/{web_visualisation/web/data → webview/web/js_autogenerated}/.gitignore +0 -0
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,OAAO,GAAG;IACf,MAAM,CAAC,GAAG,CAAC,IAAc;QACxB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,OAAO;aACL,IAAI,CAAC,oBAAoB,CAAC;aAC1B,WAAW,CAAC,gEAAgE,CAAC,CAAC;QAEhF,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/B,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnC,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC7B,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEjC,KAAK,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACD;AAED,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAElE,MAAM,OAAO,GAAG;IACf,MAAM,CAAC,GAAG,CAAC,IAAc;QACxB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;QAC9B,OAAO;aACL,IAAI,CAAC,oBAAoB,CAAC;aAC1B,WAAW,CAAC,gEAAgE,CAAC,CAAC;QAEhF,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/B,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnC,kBAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACnC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpC,aAAa,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACjC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEjC,KAAK,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACD;AAED,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { EdgeKind } from '../schema/edge.js';
2
+ /**
3
+ * How strongly each edge kind pulls its two endpoints into the same community.
4
+ * The effective weight of an edge is this coefficient times the edge's
5
+ * `metadata.count` (how many times the relationship occurs in source). The
6
+ * exception is `CALLS_RUNTIME` — the runtime call graph from `enrich` — weighted
7
+ * by its normalized sample count instead, and given a slightly higher coefficient
8
+ * because an executed call is stronger evidence of coupling than a merely
9
+ * statically-possible one. It contributes only on an enriched graph; otherwise no
10
+ * such edges exist and it is a no-op.
11
+ *
12
+ * `IMPORTS` and `EXPORTS` are deliberately absent: they are module wiring, not
13
+ * coupling, the same reasoning {@link REFERENCE_EDGE_KINDS} uses. `CONTAINS`
14
+ * carries a low weight so same-file symbols lean together without overwhelming
15
+ * call structure; drop it for modules that may cross files freely. System-level
16
+ * kinds (`READS_CONFIG`, `CALLS_EXTERNAL`, `HANDLES`) are absent because their
17
+ * targets are synthesized nodes, not code symbols.
18
+ */
19
+ export declare const CLUSTER_EDGE_WEIGHTS: Partial<Record<EdgeKind, number>>;
20
+ //# sourceMappingURL=cluster_weights.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster_weights.d.ts","sourceRoot":"","sources":["../../src/cluster/cluster_weights.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAalE,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * How strongly each edge kind pulls its two endpoints into the same community.
3
+ * The effective weight of an edge is this coefficient times the edge's
4
+ * `metadata.count` (how many times the relationship occurs in source). The
5
+ * exception is `CALLS_RUNTIME` — the runtime call graph from `enrich` — weighted
6
+ * by its normalized sample count instead, and given a slightly higher coefficient
7
+ * because an executed call is stronger evidence of coupling than a merely
8
+ * statically-possible one. It contributes only on an enriched graph; otherwise no
9
+ * such edges exist and it is a no-op.
10
+ *
11
+ * `IMPORTS` and `EXPORTS` are deliberately absent: they are module wiring, not
12
+ * coupling, the same reasoning {@link REFERENCE_EDGE_KINDS} uses. `CONTAINS`
13
+ * carries a low weight so same-file symbols lean together without overwhelming
14
+ * call structure; drop it for modules that may cross files freely. System-level
15
+ * kinds (`READS_CONFIG`, `CALLS_EXTERNAL`, `HANDLES`) are absent because their
16
+ * targets are synthesized nodes, not code symbols.
17
+ */
18
+ export const CLUSTER_EDGE_WEIGHTS = {
19
+ CALLS: 3,
20
+ CALLS_RUNTIME: 4,
21
+ INSTANTIATES: 2,
22
+ EXTENDS: 2,
23
+ IMPLEMENTS: 2,
24
+ OVERRIDES: 1.5,
25
+ WRITES: 1.5,
26
+ READS: 1,
27
+ USES_TYPE: 1,
28
+ RETURNS: 1,
29
+ PARAM_TYPE: 1,
30
+ CONTAINS: 0.5,
31
+ };
32
+ //# sourceMappingURL=cluster_weights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cluster_weights.js","sourceRoot":"","sources":["../../src/cluster/cluster_weights.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAsC;IACtE,KAAK,EAAE,CAAC;IACR,aAAa,EAAE,CAAC;IAChB,YAAY,EAAE,CAAC;IACf,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,GAAG;IACd,MAAM,EAAE,GAAG;IACX,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,GAAG;CACb,CAAC"}
@@ -0,0 +1,61 @@
1
+ /** A directed graph edge reduced to its endpoints and a pre-resolved weight. */
2
+ export type WeightedEdge = {
3
+ from: string;
4
+ to: string;
5
+ weight: number;
6
+ };
7
+ /** Tuning for a {@link CommunityDetector.detect} run. */
8
+ export type CommunityOptions = {
9
+ /** CPM resolution: higher splits the graph into more, smaller communities. */
10
+ resolution: number;
11
+ /** Leiden iterations per random start; each iteration moves, refines, and re-aggregates. */
12
+ iterations: number;
13
+ /** Independent random starts; the partition with the best CPM quality wins. */
14
+ randomStarts: number;
15
+ };
16
+ /** The outcome of a clustering run, keyed back to the caller's string node ids. */
17
+ export type CommunityResult = {
18
+ /** Node id to community index. Nodes touched by no weighted edge are absent. */
19
+ communityOf: Map<string, number>;
20
+ /** Number of communities found. */
21
+ communityCount: number;
22
+ /** CPM quality of the chosen partition. */
23
+ quality: number;
24
+ /** Member count per community, descending. */
25
+ sizes: number[];
26
+ };
27
+ /**
28
+ * Defaults tuned for module-scale community detection. `resolution` is a
29
+ * threshold on a community's average internal edge weight; 0.1 is permissive
30
+ * enough to keep loosely-coupled modules together. Sweep it up (toward 1) for
31
+ * tighter, finer clusters.
32
+ */
33
+ export declare const DEFAULT_COMMUNITY_OPTIONS: CommunityOptions;
34
+ /**
35
+ * Detects communities in the knowledge graph with the Leiden algorithm,
36
+ * delegating the optimization to `networkanalysis-ts` (the CWTS port of the
37
+ * library by the authors of the Leiden paper). The algorithm guarantees every
38
+ * community it returns is internally connected — unlike Louvain, which can leave
39
+ * a community split into disconnected pieces.
40
+ *
41
+ * This module is pure: it takes weighted edges and returns a partition, with no
42
+ * store access, mirroring how {@link RuntimeJoin} is the pure core behind
43
+ * `enrich`.
44
+ */
45
+ export declare class CommunityDetector {
46
+ /**
47
+ * Runs Leiden (CPM quality function) over a weighted, undirected projection of
48
+ * the supplied edges.
49
+ *
50
+ * Directed edges are symmetrized: every unordered endpoint pair sums its
51
+ * weights onto a single undirected edge, so a mutual call counts once with the
52
+ * combined weight. String node ids are mapped to the contiguous integer indices
53
+ * the `Network` requires, and the resulting community labels are mapped back.
54
+ */
55
+ static detect(edges: WeightedEdge[], options?: CommunityOptions): CommunityResult;
56
+ /** Assigns every node id touched by an edge a stable, contiguous integer index. */
57
+ private static indexNodes;
58
+ /** Collapses directed edges into undirected ones, summing weights per unordered pair and dropping self-loops. */
59
+ private static symmetrize;
60
+ }
61
+ //# sourceMappingURL=community_detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community_detector.d.ts","sourceRoot":"","sources":["../../src/cluster/community_detector.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,gBAAgB,GAAG;IAC9B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,mFAAmF;AACnF,MAAM,MAAM,eAAe,GAAG;IAC7B,gFAAgF;IAChF,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,EAAE,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,gBAIvC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,iBAAiB;IAC7B;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,OAAO,GAAE,gBAA4C,GAAG,eAAe;IAmD5G,mFAAmF;IACnF,OAAO,CAAC,MAAM,CAAC,UAAU;IAczB,iHAAiH;IACjH,OAAO,CAAC,MAAM,CAAC,UAAU;CA2BzB"}
@@ -0,0 +1,120 @@
1
+ import { Clustering, LeidenAlgorithm, Network } from 'networkanalysis-ts';
2
+ /**
3
+ * Defaults tuned for module-scale community detection. `resolution` is a
4
+ * threshold on a community's average internal edge weight; 0.1 is permissive
5
+ * enough to keep loosely-coupled modules together. Sweep it up (toward 1) for
6
+ * tighter, finer clusters.
7
+ */
8
+ export const DEFAULT_COMMUNITY_OPTIONS = {
9
+ resolution: 0.1,
10
+ iterations: 10,
11
+ randomStarts: 10,
12
+ };
13
+ /**
14
+ * Detects communities in the knowledge graph with the Leiden algorithm,
15
+ * delegating the optimization to `networkanalysis-ts` (the CWTS port of the
16
+ * library by the authors of the Leiden paper). The algorithm guarantees every
17
+ * community it returns is internally connected — unlike Louvain, which can leave
18
+ * a community split into disconnected pieces.
19
+ *
20
+ * This module is pure: it takes weighted edges and returns a partition, with no
21
+ * store access, mirroring how {@link RuntimeJoin} is the pure core behind
22
+ * `enrich`.
23
+ */
24
+ export class CommunityDetector {
25
+ /**
26
+ * Runs Leiden (CPM quality function) over a weighted, undirected projection of
27
+ * the supplied edges.
28
+ *
29
+ * Directed edges are symmetrized: every unordered endpoint pair sums its
30
+ * weights onto a single undirected edge, so a mutual call counts once with the
31
+ * combined weight. String node ids are mapped to the contiguous integer indices
32
+ * the `Network` requires, and the resulting community labels are mapped back.
33
+ */
34
+ static detect(edges, options = DEFAULT_COMMUNITY_OPTIONS) {
35
+ const { ids, indexOf } = CommunityDetector.indexNodes(edges);
36
+ if (ids.length === 0) {
37
+ return { communityOf: new Map(), communityCount: 0, quality: 0, sizes: [] };
38
+ }
39
+ const undirected = CommunityDetector.symmetrize(edges, indexOf);
40
+ // Uniform node weights (not degree) keep this classic CPM: the resolution is
41
+ // then a portable threshold on a community's average internal edge weight,
42
+ // independent of node degree. Degree weights make the usable resolution range
43
+ // collapse to a graph-specific sliver.
44
+ const network = new Network({
45
+ nNodes: ids.length,
46
+ setNodeWeightsToTotalEdgeWeights: false,
47
+ edges: [undirected.sources, undirected.targets],
48
+ edgeWeights: undirected.weights,
49
+ sortedEdges: false,
50
+ checkIntegrity: false,
51
+ });
52
+ const algorithm = new LeidenAlgorithm();
53
+ algorithm.setResolution(options.resolution);
54
+ algorithm.setNIterations(options.iterations);
55
+ let best;
56
+ let bestQuality = Number.NEGATIVE_INFINITY;
57
+ for (let start = 0; start < options.randomStarts; start += 1) {
58
+ const clustering = new Clustering({ nNodes: network.getNNodes() });
59
+ algorithm.improveClustering(network, clustering);
60
+ const quality = algorithm.calcQuality(network, clustering);
61
+ if (quality > bestQuality) {
62
+ best = clustering;
63
+ bestQuality = quality;
64
+ }
65
+ }
66
+ if (best === undefined) {
67
+ return { communityOf: new Map(), communityCount: 0, quality: 0, sizes: [] };
68
+ }
69
+ best.orderClustersByNNodes();
70
+ const labels = best.getClusters();
71
+ const communityOf = new Map();
72
+ ids.forEach((id, i) => communityOf.set(id, labels[i]));
73
+ return {
74
+ communityOf,
75
+ communityCount: best.getNClusters(),
76
+ quality: bestQuality,
77
+ sizes: best.getNNodesPerCluster(),
78
+ };
79
+ }
80
+ /** Assigns every node id touched by an edge a stable, contiguous integer index. */
81
+ static indexNodes(edges) {
82
+ const indexOf = new Map();
83
+ const ids = [];
84
+ for (const edge of edges) {
85
+ for (const id of [edge.from, edge.to]) {
86
+ if (indexOf.has(id) === false) {
87
+ indexOf.set(id, ids.length);
88
+ ids.push(id);
89
+ }
90
+ }
91
+ }
92
+ return { ids, indexOf };
93
+ }
94
+ /** Collapses directed edges into undirected ones, summing weights per unordered pair and dropping self-loops. */
95
+ static symmetrize(edges, indexOf) {
96
+ const merged = new Map();
97
+ for (const edge of edges) {
98
+ const a = indexOf.get(edge.from);
99
+ const b = indexOf.get(edge.to);
100
+ if (a === undefined || b === undefined || a === b) {
101
+ continue;
102
+ }
103
+ const low = a < b ? a : b;
104
+ const high = a < b ? b : a;
105
+ const key = `${low}:${high}`;
106
+ merged.set(key, (merged.get(key) ?? 0) + edge.weight);
107
+ }
108
+ const sources = [];
109
+ const targets = [];
110
+ const weights = [];
111
+ for (const [key, weight] of merged) {
112
+ const separator = key.indexOf(':');
113
+ sources.push(Number(key.slice(0, separator)));
114
+ targets.push(Number(key.slice(separator + 1)));
115
+ weights.push(weight);
116
+ }
117
+ return { sources, targets, weights };
118
+ }
119
+ }
120
+ //# sourceMappingURL=community_detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community_detector.js","sourceRoot":"","sources":["../../src/cluster/community_detector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AA+B1E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAqB;IAC1D,UAAU,EAAE,GAAG;IACf,UAAU,EAAE,EAAE;IACd,YAAY,EAAE,EAAE;CAChB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,OAAO,iBAAiB;IAC7B;;;;;;;;OAQG;IACH,MAAM,CAAC,MAAM,CAAC,KAAqB,EAAE,UAA4B,yBAAyB;QACzF,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC7D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,WAAW,EAAE,IAAI,GAAG,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC7E,CAAC;QACD,MAAM,UAAU,GAAG,iBAAiB,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAEhE,6EAA6E;QAC7E,2EAA2E;QAC3E,8EAA8E;QAC9E,uCAAuC;QACvC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;YAC3B,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,gCAAgC,EAAE,KAAK;YACvC,KAAK,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC;YAC/C,WAAW,EAAE,UAAU,CAAC,OAAO;YAC/B,WAAW,EAAE,KAAK;YAClB,cAAc,EAAE,KAAK;SACrB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,IAAI,eAAe,EAAE,CAAC;QACxC,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC5C,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAE7C,IAAI,IAA4B,CAAC;QACjC,IAAI,WAAW,GAAG,MAAM,CAAC,iBAAiB,CAAC;QAC3C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,YAAY,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YAC9D,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACnE,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAC3D,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC3B,IAAI,GAAG,UAAU,CAAC;gBAClB,WAAW,GAAG,OAAO,CAAC;YACvB,CAAC;QACF,CAAC;QACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,EAAE,WAAW,EAAE,IAAI,GAAG,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC7E,CAAC;QACD,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC9C,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO;YACN,WAAW;YACX,cAAc,EAAE,IAAI,CAAC,YAAY,EAAE;YACnC,OAAO,EAAE,WAAW;YACpB,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE;SACjC,CAAC;IACH,CAAC;IAED,mFAAmF;IAC3E,MAAM,CAAC,UAAU,CAAC,KAAqB;QAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC1C,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,KAAK,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACvC,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,KAAK,EAAE,CAAC;oBAC/B,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;oBAC5B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACd,CAAC;YACF,CAAC;QACF,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;IACzB,CAAC;IAED,iHAAiH;IACzG,MAAM,CAAC,UAAU,CACxB,KAAqB,EACrB,OAA4B;QAE5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACnD,SAAS;YACV,CAAC;YACD,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;YAC9C,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACtC,CAAC;CACD"}
@@ -0,0 +1,84 @@
1
+ import { WeightedEdge } from './community_detector.js';
2
+ /** The subset of a node's fields the labeler reasons over. */
3
+ export type LabelableNode = {
4
+ id: string;
5
+ name: string;
6
+ kind: string;
7
+ filePath: string;
8
+ };
9
+ /** Everything {@link CommunityLabeler.label} needs: the partition, the nodes, and the weighted edges. */
10
+ export type CommunityLabelInput = {
11
+ /** Node id to community index, as produced by {@link CommunityDetector.detect}. */
12
+ communityOf: Map<string, number>;
13
+ /** The clustered nodes; only members present in `communityOf` are labelled. */
14
+ nodes: LabelableNode[];
15
+ /** The weighted edges the clustering ran on, used to find each community's hub. */
16
+ edges: WeightedEdge[];
17
+ };
18
+ /**
19
+ * Derives a human-readable label for each community from its members — a
20
+ * deterministic, dependency-free alternative to the bare ordinal (`community 3`).
21
+ *
22
+ * The label is a composite of two signals available on every node:
23
+ * - the **dominant directory** the members share (code communities track module
24
+ * structure), and
25
+ * - the **hub member** — the node with the highest internal (within-community)
26
+ * weighted degree, i.e. the symbol the rest of the community couples to most.
27
+ *
28
+ * Because every part is derived from membership (never the ordinal index), the
29
+ * same group of nodes earns the same label across the algorithm's stochastic
30
+ * re-runs. This module is pure — it takes a partition and returns labels, with no
31
+ * store access — mirroring {@link CommunityDetector}.
32
+ */
33
+ export declare class CommunityLabeler {
34
+ /**
35
+ * Returns a map from community index to a unique, human-readable label.
36
+ *
37
+ * Per community: if the members live in a single file, the label is that file's
38
+ * base name; otherwise it is `<directory> · <hub>` when the members concentrate
39
+ * in one directory, or the hub alone when they are scattered. Colliding labels
40
+ * are disambiguated with the hub, then the ordinal, so the result is injective.
41
+ */
42
+ static label(input: CommunityLabelInput): Map<number, string>;
43
+ /** Buckets the labelled nodes by their community index, dropping nodes absent from the partition. */
44
+ private static groupMembers;
45
+ /**
46
+ * Sums each node's weighted degree over edges that stay inside its community.
47
+ * Cross-community edges and self-loops are ignored, so the result ranks members
48
+ * by how strongly they couple to the rest of their own community.
49
+ */
50
+ private static internalDegree;
51
+ /**
52
+ * The name of the member with the greatest internal weighted degree, breaking
53
+ * ties by name then id so the choice is stable. Returns `undefined` only for an
54
+ * empty group.
55
+ */
56
+ private static hubName;
57
+ /**
58
+ * A node's human-facing name. `Module` nodes carry their file path as their
59
+ * name, which reads poorly as a label, so they display as the file's base name.
60
+ */
61
+ private static displayName;
62
+ /** Builds the un-deduplicated label for one community from its area and hub. */
63
+ private static composeLabel;
64
+ /**
65
+ * The directory most members share, with the fraction of members under it. Ties
66
+ * favour the shorter (shallower) path, then lexical order, for determinism.
67
+ */
68
+ private static dominantDirectory;
69
+ /**
70
+ * Resolves label collisions into a unique set: a duplicated label gains its
71
+ * community's hub (when that adds information), and any still-colliding label
72
+ * gains a `#index` suffix as a last resort.
73
+ */
74
+ private static dedupe;
75
+ /** Applies `rename` to every label that more than one community currently shares. */
76
+ private static disambiguate;
77
+ /** The directory portion of a POSIX-style path, or `''` for a bare file name. */
78
+ private static directoryOf;
79
+ /** The final segment of a directory path, or the whole path when it has no separator. */
80
+ private static lastSegment;
81
+ /** A file's base name without its directory or extension (`src/a/foo.ts` → `foo`). */
82
+ private static baseName;
83
+ }
84
+ //# sourceMappingURL=community_labeler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community_labeler.d.ts","sourceRoot":"","sources":["../../src/cluster/community_labeler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,8DAA8D;AAC9D,MAAM,MAAM,aAAa,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,yGAAyG;AACzG,MAAM,MAAM,mBAAmB,GAAG;IACjC,mFAAmF;IACnF,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,+EAA+E;IAC/E,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,mFAAmF;IACnF,KAAK,EAAE,YAAY,EAAE,CAAC;CACtB,CAAC;AASF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAc7D,qGAAqG;IACrG,OAAO,CAAC,MAAM,CAAC,YAAY;IAiB3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;IAiB7B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,OAAO;IAkBtB;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,YAAY;IAoB3B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAmBhC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,MAAM;IAQrB,qFAAqF;IACrF,OAAO,CAAC,MAAM,CAAC,YAAY;IAe3B,iFAAiF;IACjF,OAAO,CAAC,MAAM,CAAC,WAAW;IAK1B,yFAAyF;IACzF,OAAO,CAAC,MAAM,CAAC,WAAW;IAQ1B,sFAAsF;IACtF,OAAO,CAAC,MAAM,CAAC,QAAQ;CAMvB"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Fraction of a community's members that must share one directory before that
3
+ * directory is treated as the community's "area". Below this the members are too
4
+ * scattered for the directory to be meaningful, so the label falls back to the hub.
5
+ */
6
+ const DIRECTORY_PURITY_THRESHOLD = 0.6;
7
+ /**
8
+ * Derives a human-readable label for each community from its members — a
9
+ * deterministic, dependency-free alternative to the bare ordinal (`community 3`).
10
+ *
11
+ * The label is a composite of two signals available on every node:
12
+ * - the **dominant directory** the members share (code communities track module
13
+ * structure), and
14
+ * - the **hub member** — the node with the highest internal (within-community)
15
+ * weighted degree, i.e. the symbol the rest of the community couples to most.
16
+ *
17
+ * Because every part is derived from membership (never the ordinal index), the
18
+ * same group of nodes earns the same label across the algorithm's stochastic
19
+ * re-runs. This module is pure — it takes a partition and returns labels, with no
20
+ * store access — mirroring {@link CommunityDetector}.
21
+ */
22
+ export class CommunityLabeler {
23
+ /**
24
+ * Returns a map from community index to a unique, human-readable label.
25
+ *
26
+ * Per community: if the members live in a single file, the label is that file's
27
+ * base name; otherwise it is `<directory> · <hub>` when the members concentrate
28
+ * in one directory, or the hub alone when they are scattered. Colliding labels
29
+ * are disambiguated with the hub, then the ordinal, so the result is injective.
30
+ */
31
+ static label(input) {
32
+ const groups = CommunityLabeler.groupMembers(input.communityOf, input.nodes);
33
+ const internalDegree = CommunityLabeler.internalDegree(input.communityOf, input.edges);
34
+ const base = new Map();
35
+ const hubs = new Map();
36
+ for (const [index, members] of groups) {
37
+ const hub = CommunityLabeler.hubName(members, internalDegree);
38
+ hubs.set(index, hub);
39
+ base.set(index, CommunityLabeler.composeLabel(index, members, hub));
40
+ }
41
+ return CommunityLabeler.dedupe(base, hubs);
42
+ }
43
+ /** Buckets the labelled nodes by their community index, dropping nodes absent from the partition. */
44
+ static groupMembers(communityOf, nodes) {
45
+ const groups = new Map();
46
+ for (const node of nodes) {
47
+ const index = communityOf.get(node.id);
48
+ if (index === undefined) {
49
+ continue;
50
+ }
51
+ const members = groups.get(index) ?? [];
52
+ members.push(node);
53
+ groups.set(index, members);
54
+ }
55
+ return groups;
56
+ }
57
+ /**
58
+ * Sums each node's weighted degree over edges that stay inside its community.
59
+ * Cross-community edges and self-loops are ignored, so the result ranks members
60
+ * by how strongly they couple to the rest of their own community.
61
+ */
62
+ static internalDegree(communityOf, edges) {
63
+ const degree = new Map();
64
+ for (const edge of edges) {
65
+ if (edge.from === edge.to) {
66
+ continue;
67
+ }
68
+ const from = communityOf.get(edge.from);
69
+ const to = communityOf.get(edge.to);
70
+ if (from === undefined || to === undefined || from !== to) {
71
+ continue;
72
+ }
73
+ degree.set(edge.from, (degree.get(edge.from) ?? 0) + edge.weight);
74
+ degree.set(edge.to, (degree.get(edge.to) ?? 0) + edge.weight);
75
+ }
76
+ return degree;
77
+ }
78
+ /**
79
+ * The name of the member with the greatest internal weighted degree, breaking
80
+ * ties by name then id so the choice is stable. Returns `undefined` only for an
81
+ * empty group.
82
+ */
83
+ static hubName(members, internalDegree) {
84
+ if (members.length === 0) {
85
+ return undefined;
86
+ }
87
+ const ranked = [...members].sort((a, b) => {
88
+ const degreeA = internalDegree.get(a.id) ?? 0;
89
+ const degreeB = internalDegree.get(b.id) ?? 0;
90
+ if (degreeB !== degreeA) {
91
+ return degreeB - degreeA;
92
+ }
93
+ if (a.name !== b.name) {
94
+ return a.name < b.name ? -1 : 1;
95
+ }
96
+ return a.id < b.id ? -1 : 1;
97
+ });
98
+ return CommunityLabeler.displayName(ranked[0]);
99
+ }
100
+ /**
101
+ * A node's human-facing name. `Module` nodes carry their file path as their
102
+ * name, which reads poorly as a label, so they display as the file's base name.
103
+ */
104
+ static displayName(node) {
105
+ return node.kind === 'Module' ? CommunityLabeler.baseName(node.filePath) : node.name;
106
+ }
107
+ /** Builds the un-deduplicated label for one community from its area and hub. */
108
+ static composeLabel(index, members, hub) {
109
+ if (members.length === 0) {
110
+ return `community ${index}`;
111
+ }
112
+ const files = new Set(members.map((member) => member.filePath));
113
+ if (files.size === 1) {
114
+ const fileName = CommunityLabeler.baseName(members[0].filePath);
115
+ return fileName.length > 0 ? fileName : (hub ?? `community ${index}`);
116
+ }
117
+ const { directory, purity } = CommunityLabeler.dominantDirectory(members);
118
+ const area = CommunityLabeler.lastSegment(directory);
119
+ if (area.length > 0 && purity >= DIRECTORY_PURITY_THRESHOLD) {
120
+ return hub === undefined ? area : `${area} · ${hub}`;
121
+ }
122
+ if (hub !== undefined) {
123
+ return hub;
124
+ }
125
+ return area.length > 0 ? area : `community ${index}`;
126
+ }
127
+ /**
128
+ * The directory most members share, with the fraction of members under it. Ties
129
+ * favour the shorter (shallower) path, then lexical order, for determinism.
130
+ */
131
+ static dominantDirectory(members) {
132
+ const counts = new Map();
133
+ for (const member of members) {
134
+ const directory = CommunityLabeler.directoryOf(member.filePath);
135
+ counts.set(directory, (counts.get(directory) ?? 0) + 1);
136
+ }
137
+ const ranked = [...counts.entries()].sort((a, b) => {
138
+ if (b[1] !== a[1]) {
139
+ return b[1] - a[1];
140
+ }
141
+ if (a[0].length !== b[0].length) {
142
+ return a[0].length - b[0].length;
143
+ }
144
+ return a[0] < b[0] ? -1 : 1;
145
+ });
146
+ const [directory, count] = ranked[0];
147
+ return { directory, purity: count / members.length };
148
+ }
149
+ /**
150
+ * Resolves label collisions into a unique set: a duplicated label gains its
151
+ * community's hub (when that adds information), and any still-colliding label
152
+ * gains a `#index` suffix as a last resort.
153
+ */
154
+ static dedupe(base, hubs) {
155
+ const withHub = CommunityLabeler.disambiguate(base, (index, label) => {
156
+ const hub = hubs.get(index);
157
+ return hub !== undefined && label.includes(hub) === false ? `${label} · ${hub}` : label;
158
+ });
159
+ return CommunityLabeler.disambiguate(withHub, (index, label) => `${label} #${index}`);
160
+ }
161
+ /** Applies `rename` to every label that more than one community currently shares. */
162
+ static disambiguate(labels, rename) {
163
+ const counts = new Map();
164
+ for (const label of labels.values()) {
165
+ counts.set(label, (counts.get(label) ?? 0) + 1);
166
+ }
167
+ const result = new Map();
168
+ for (const [index, label] of labels) {
169
+ result.set(index, (counts.get(label) ?? 0) > 1 ? rename(index, label) : label);
170
+ }
171
+ return result;
172
+ }
173
+ /** The directory portion of a POSIX-style path, or `''` for a bare file name. */
174
+ static directoryOf(filePath) {
175
+ const slash = filePath.lastIndexOf('/');
176
+ return slash === -1 ? '' : filePath.slice(0, slash);
177
+ }
178
+ /** The final segment of a directory path, or the whole path when it has no separator. */
179
+ static lastSegment(directory) {
180
+ if (directory.length === 0) {
181
+ return '';
182
+ }
183
+ const slash = directory.lastIndexOf('/');
184
+ return slash === -1 ? directory : directory.slice(slash + 1);
185
+ }
186
+ /** A file's base name without its directory or extension (`src/a/foo.ts` → `foo`). */
187
+ static baseName(filePath) {
188
+ const slash = filePath.lastIndexOf('/');
189
+ const fileName = slash === -1 ? filePath : filePath.slice(slash + 1);
190
+ const dot = fileName.lastIndexOf('.');
191
+ return dot <= 0 ? fileName : fileName.slice(0, dot);
192
+ }
193
+ }
194
+ //# sourceMappingURL=community_labeler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community_labeler.js","sourceRoot":"","sources":["../../src/cluster/community_labeler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { EdgeKind } from '../schema/edge.js';
2
+ import { KuzuStore } from '../store/kuzu_store.js';
3
+ import { CommunityOptions } from './community_detector.js';
4
+ /** Namespaced key under which a node's community index is stored on its metadata. */
5
+ export declare const COMMUNITY_METADATA_KEY = "community";
6
+ /** Namespaced key under which a node's human-readable community label is stored on its metadata. */
7
+ export declare const COMMUNITY_LABEL_METADATA_KEY = "communityLabel";
8
+ /** Graph-level metadata key under which the clustering manifest is recorded. */
9
+ export declare const CLUSTERING_MANIFEST_KEY = "clustering";
10
+ /** The summary `cluster` returns and prints. */
11
+ export type ClusterReport = {
12
+ nodesAssigned: number;
13
+ communityCount: number;
14
+ quality: number;
15
+ resolution: number;
16
+ /** Member count per community, descending. */
17
+ sizes: number[];
18
+ /** Human-readable label per community, aligned with {@link ClusterReport.sizes}. */
19
+ labels: string[];
20
+ };
21
+ /**
22
+ * Orchestrates a clustering pass over a loaded graph: read the weighted edges,
23
+ * detect communities with {@link CommunityDetector}, and attach the community
24
+ * index onto each node's metadata. Mirrors {@link RuntimeEnricher} — the pure
25
+ * algorithm lives in {@link CommunityDetector}; this class owns the store I/O.
26
+ *
27
+ * Existing node metadata is preserved; only the `community` key is written, so
28
+ * re-running is idempotent for an unchanged graph.
29
+ */
30
+ export declare class GraphClusterer {
31
+ static cluster(store: KuzuStore, weights?: Partial<Record<EdgeKind, number>>, options?: CommunityOptions): Promise<ClusterReport>;
32
+ /** Flattens the per-index label map into an array aligned with community indices `0..count-1`. */
33
+ private static orderLabels;
34
+ /**
35
+ * Reads every edge whose kind carries a positive weight, resolving each to a
36
+ * {@link WeightedEdge} whose weight is the kind's coefficient times the edge's
37
+ * call-site `count`.
38
+ */
39
+ private static readWeightedEdges;
40
+ /** Decodes an edge's call-site `count`, defaulting to 1 (the minimum the builder records). */
41
+ private static callCount;
42
+ /** Decodes a runtime call edge's `samples` weight, defaulting to 0. */
43
+ private static edgeSamples;
44
+ /** Decodes the JSON `metadata` column into a record; a missing, empty, or malformed value yields an empty record. */
45
+ private static parseMetadata;
46
+ }
47
+ //# sourceMappingURL=graph_clusterer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph_clusterer.d.ts","sourceRoot":"","sources":["../../src/cluster/graph_clusterer.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAqB,gBAAgB,EAA2C,MAAM,yBAAyB,CAAC;AAGvH,qFAAqF;AACrF,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAElD,oGAAoG;AACpG,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,gFAAgF;AAChF,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAKpD,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oFAAoF;IACpF,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF;;;;;;;;GAQG;AACH,qBAAa,cAAc;WACb,OAAO,CACnB,KAAK,EAAE,SAAS,EAChB,OAAO,GAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAM,EAC/C,OAAO,GAAE,gBAA4C,GACnD,OAAO,CAAC,aAAa,CAAC;IAgDzB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B;;;;OAIG;mBACkB,iBAAiB;IAwCtC,8FAA8F;IAC9F,OAAO,CAAC,MAAM,CAAC,SAAS;IAKxB,uEAAuE;IACvE,OAAO,CAAC,MAAM,CAAC,WAAW;IAK1B,qHAAqH;IACrH,OAAO,CAAC,MAAM,CAAC,aAAa;CAW5B"}