@ttsc/graph 0.30.4 → 0.31.1

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 (292) hide show
  1. package/README.md +122 -30
  2. package/lib/CapturedProcessOutput.d.ts +47 -0
  3. package/lib/CapturedProcessOutput.js +3 -0
  4. package/lib/CapturedProcessOutput.js.map +1 -0
  5. package/lib/ILauncherOption.d.ts +22 -0
  6. package/lib/ILauncherOption.js +3 -0
  7. package/lib/ILauncherOption.js.map +1 -0
  8. package/lib/IProjectOptions.d.ts +18 -0
  9. package/lib/IProjectOptions.js +3 -0
  10. package/lib/IProjectOptions.js.map +1 -0
  11. package/lib/ParsedLauncherOptions.d.ts +13 -0
  12. package/lib/ParsedLauncherOptions.js +3 -0
  13. package/lib/ParsedLauncherOptions.js.map +1 -0
  14. package/lib/TtscGraphApplication.d.ts +38 -1
  15. package/lib/TtscGraphApplication.js +26 -1
  16. package/lib/TtscGraphApplication.js.map +1 -1
  17. package/lib/TtscGraphLauncherArguments.d.ts +115 -0
  18. package/lib/TtscGraphLauncherArguments.js +181 -0
  19. package/lib/TtscGraphLauncherArguments.js.map +1 -0
  20. package/lib/TtscGraphViewSnapshot.d.ts +35 -0
  21. package/lib/TtscGraphViewSnapshot.js +48 -0
  22. package/lib/TtscGraphViewSnapshot.js.map +1 -0
  23. package/lib/index.d.ts +9 -24
  24. package/lib/index.js +6 -128
  25. package/lib/index.js.map +1 -1
  26. package/lib/internal/GraphProcessTrace.d.ts +1 -0
  27. package/lib/internal/GraphProcessTrace.js +235 -0
  28. package/lib/internal/GraphProcessTrace.js.map +1 -0
  29. package/lib/launcherArgs.d.ts +75 -16
  30. package/lib/launcherArgs.js +77 -8
  31. package/lib/launcherArgs.js.map +1 -1
  32. package/lib/model/IArtifactDirectory.d.ts +26 -0
  33. package/lib/model/IArtifactDirectory.js +3 -0
  34. package/lib/model/IArtifactDirectory.js.map +1 -0
  35. package/lib/model/IArtifactInputs.d.ts +19 -0
  36. package/lib/model/IArtifactInputs.js +3 -0
  37. package/lib/model/IArtifactInputs.js.map +1 -0
  38. package/lib/model/IPublishedArtifacts.d.ts +76 -0
  39. package/lib/model/IPublishedArtifacts.js +3 -0
  40. package/lib/model/IPublishedArtifacts.js.map +1 -0
  41. package/lib/model/IPublishedArtifactsResident.d.ts +19 -0
  42. package/lib/model/IPublishedArtifactsResident.js +3 -0
  43. package/lib/model/IPublishedArtifactsResident.js.map +1 -0
  44. package/lib/model/ITtscLintDaemonTarget.d.ts +20 -0
  45. package/lib/model/ITtscLintDaemonTarget.js +3 -0
  46. package/lib/model/ITtscLintDaemonTarget.js.map +1 -0
  47. package/lib/model/TtscGraphLinePeer.d.ts +146 -0
  48. package/lib/model/TtscGraphLinePeer.js +213 -0
  49. package/lib/model/TtscGraphLinePeer.js.map +1 -0
  50. package/lib/model/TtscGraphMemory.d.ts +156 -16
  51. package/lib/model/TtscGraphMemory.js +228 -222
  52. package/lib/model/TtscGraphMemory.js.map +1 -1
  53. package/lib/model/TtscGraphNativeArguments.d.ts +40 -0
  54. package/lib/model/TtscGraphNativeArguments.js +64 -0
  55. package/lib/model/TtscGraphNativeArguments.js.map +1 -0
  56. package/lib/model/TtscGraphNodeId.d.ts +55 -3
  57. package/lib/model/TtscGraphNodeId.js +40 -2
  58. package/lib/model/TtscGraphNodeId.js.map +1 -1
  59. package/lib/model/TtscGraphProjection.d.ts +156 -0
  60. package/lib/model/TtscGraphProjection.js +390 -0
  61. package/lib/model/TtscGraphProjection.js.map +1 -0
  62. package/lib/model/TtscGraphProtocol.d.ts +28 -0
  63. package/lib/model/TtscGraphProtocol.js +928 -0
  64. package/lib/model/TtscGraphProtocol.js.map +1 -0
  65. package/lib/model/TtscGraphReadonly.d.ts +15 -0
  66. package/lib/model/TtscGraphReadonly.js +3 -0
  67. package/lib/model/TtscGraphReadonly.js.map +1 -0
  68. package/lib/model/TtscGraphRequestOptions.d.ts +16 -0
  69. package/lib/model/TtscGraphRequestOptions.js +3 -0
  70. package/lib/model/TtscGraphRequestOptions.js.map +1 -0
  71. package/lib/model/TtscGraphSession.d.ts +82 -36
  72. package/lib/model/TtscGraphSession.js +164 -1238
  73. package/lib/model/TtscGraphSession.js.map +1 -1
  74. package/lib/model/TtscGraphSessionOptions.d.ts +20 -0
  75. package/lib/model/TtscGraphSessionOptions.js +3 -0
  76. package/lib/model/TtscGraphSessionOptions.js.map +1 -0
  77. package/lib/model/TtscGraphSessionState.d.ts +187 -0
  78. package/lib/model/TtscGraphSessionState.js +417 -0
  79. package/lib/model/TtscGraphSessionState.js.map +1 -0
  80. package/lib/model/TtscGraphShardStore.d.ts +64 -3
  81. package/lib/model/TtscGraphShardStore.js +85 -15
  82. package/lib/model/TtscGraphShardStore.js.map +1 -1
  83. package/lib/model/TtscGraphSourceReader.d.ts +31 -8
  84. package/lib/model/TtscGraphSourceReader.js +37 -13
  85. package/lib/model/TtscGraphSourceReader.js.map +1 -1
  86. package/lib/model/TtscLintDaemon.d.ts +35 -17
  87. package/lib/model/TtscLintDaemon.js +39 -117
  88. package/lib/model/TtscLintDaemon.js.map +1 -1
  89. package/lib/model/TtscLintDaemonState.d.ts +69 -0
  90. package/lib/model/TtscLintDaemonState.js +166 -0
  91. package/lib/model/TtscLintDaemonState.js.map +1 -0
  92. package/lib/model/copyGraphRecords.d.ts +14 -0
  93. package/lib/model/copyGraphRecords.js +19 -0
  94. package/lib/model/copyGraphRecords.js.map +1 -0
  95. package/lib/model/copyGraphSnapshot.d.ts +17 -0
  96. package/lib/model/copyGraphSnapshot.js +33 -0
  97. package/lib/model/copyGraphSnapshot.js.map +1 -0
  98. package/lib/model/loadGraph.d.ts +19 -10
  99. package/lib/model/loadGraph.js +21 -12
  100. package/lib/model/loadGraph.js.map +1 -1
  101. package/lib/model/publishedArtifacts.d.ts +78 -97
  102. package/lib/model/publishedArtifacts.js +271 -85
  103. package/lib/model/publishedArtifacts.js.map +1 -1
  104. package/lib/nativeExecutable.d.ts +19 -8
  105. package/lib/nativeExecutable.js +75 -15
  106. package/lib/nativeExecutable.js.map +1 -1
  107. package/lib/reduce.d.ts +19 -45
  108. package/lib/reduce.js +19 -3
  109. package/lib/reduce.js.map +1 -1
  110. package/lib/resolveGraphBinary.d.ts +9 -0
  111. package/lib/resolveGraphBinary.js +11 -3
  112. package/lib/resolveGraphBinary.js.map +1 -1
  113. package/lib/runGraph.d.ts +21 -0
  114. package/lib/runGraph.js +83 -0
  115. package/lib/runGraph.js.map +1 -0
  116. package/lib/server/IResolvedGraphHandle.d.ts +22 -0
  117. package/lib/server/IResolvedGraphHandle.js +3 -0
  118. package/lib/server/IResolvedGraphHandle.js.map +1 -0
  119. package/lib/server/createServer.d.ts +14 -10
  120. package/lib/server/createServer.js +42 -39
  121. package/lib/server/createServer.js.map +1 -1
  122. package/lib/server/exportSurface.d.ts +23 -1
  123. package/lib/server/exportSurface.js +23 -1
  124. package/lib/server/exportSurface.js.map +1 -1
  125. package/lib/server/formatGraphFailure.d.ts +20 -0
  126. package/lib/server/formatGraphFailure.js +35 -0
  127. package/lib/server/formatGraphFailure.js.map +1 -0
  128. package/lib/server/pathPolicy.d.ts +70 -5
  129. package/lib/server/pathPolicy.js +66 -4
  130. package/lib/server/pathPolicy.js.map +1 -1
  131. package/lib/server/resolveHandle.d.ts +10 -5
  132. package/lib/server/resolveHandle.js +39 -7
  133. package/lib/server/resolveHandle.js.map +1 -1
  134. package/lib/server/resultAudit.d.ts +17 -14
  135. package/lib/server/resultAudit.js +56 -27
  136. package/lib/server/resultAudit.js.map +1 -1
  137. package/lib/server/resultNext.d.ts +26 -1
  138. package/lib/server/resultNext.js +14 -0
  139. package/lib/server/resultNext.js.map +1 -1
  140. package/lib/server/runDetails.d.ts +72 -15
  141. package/lib/server/runDetails.js +104 -69
  142. package/lib/server/runDetails.js.map +1 -1
  143. package/lib/server/runEntrypoints.d.ts +9 -0
  144. package/lib/server/runEntrypoints.js +9 -0
  145. package/lib/server/runEntrypoints.js.map +1 -1
  146. package/lib/server/runLookup.d.ts +13 -3
  147. package/lib/server/runLookup.js +19 -6
  148. package/lib/server/runLookup.js.map +1 -1
  149. package/lib/server/runOverview.d.ts +13 -4
  150. package/lib/server/runOverview.js +13 -4
  151. package/lib/server/runOverview.js.map +1 -1
  152. package/lib/server/runTour.d.ts +9 -0
  153. package/lib/server/runTour.js +33 -65
  154. package/lib/server/runTour.js.map +1 -1
  155. package/lib/server/runTrace.d.ts +25 -1
  156. package/lib/server/runTrace.js +133 -88
  157. package/lib/server/runTrace.js.map +1 -1
  158. package/lib/server/startServer.d.ts +16 -0
  159. package/lib/server/startServer.js +24 -1
  160. package/lib/server/startServer.js.map +1 -1
  161. package/lib/structures/ITtscGraphApplication.d.ts +95 -41
  162. package/lib/structures/ITtscGraphDecorator.d.ts +18 -0
  163. package/lib/structures/ITtscGraphDetails.d.ts +81 -6
  164. package/lib/structures/ITtscGraphDocTag.d.ts +17 -8
  165. package/lib/structures/ITtscGraphDump.d.ts +106 -8
  166. package/lib/structures/ITtscGraphEdge.d.ts +14 -2
  167. package/lib/structures/ITtscGraphEntrypoints.d.ts +81 -6
  168. package/lib/structures/ITtscGraphEscape.d.ts +24 -2
  169. package/lib/structures/ITtscGraphEvidence.d.ts +9 -0
  170. package/lib/structures/ITtscGraphLookup.d.ts +42 -9
  171. package/lib/structures/ITtscGraphNext.d.ts +12 -1
  172. package/lib/structures/ITtscGraphNode.d.ts +38 -11
  173. package/lib/structures/ITtscGraphOverview.d.ts +81 -6
  174. package/lib/structures/ITtscGraphSnapshot.d.ts +97 -2
  175. package/lib/structures/ITtscGraphSpan.d.ts +9 -0
  176. package/lib/structures/ITtscGraphTour.d.ts +83 -9
  177. package/lib/structures/ITtscGraphTrace.d.ts +99 -14
  178. package/lib/structures/RawDump.d.ts +22 -0
  179. package/lib/structures/RawDump.js +3 -0
  180. package/lib/structures/RawDump.js.map +1 -0
  181. package/lib/structures/RawEdge.d.ts +20 -0
  182. package/lib/structures/RawEdge.js +3 -0
  183. package/lib/structures/RawEdge.js.map +1 -0
  184. package/lib/structures/RawNode.d.ts +26 -0
  185. package/lib/structures/RawNode.js +3 -0
  186. package/lib/structures/RawNode.js.map +1 -0
  187. package/lib/structures/TtscGraphArtifactNodeKind.d.ts +9 -0
  188. package/lib/structures/TtscGraphArtifactNodeKind.js +9 -0
  189. package/lib/structures/TtscGraphArtifactNodeKind.js.map +1 -1
  190. package/lib/structures/TtscGraphDumpEdgeKind.d.ts +15 -1
  191. package/lib/structures/TtscGraphDumpNodeKind.d.ts +9 -0
  192. package/lib/structures/TtscGraphEdgeKind.d.ts +13 -4
  193. package/lib/structures/TtscGraphNodeKind.d.ts +9 -0
  194. package/lib/structures/TtscGraphNodeModifier.d.ts +9 -0
  195. package/lib/structures/ViewerLink.d.ts +20 -0
  196. package/lib/structures/ViewerLink.js +3 -0
  197. package/lib/structures/ViewerLink.js.map +1 -0
  198. package/lib/structures/ViewerNode.d.ts +24 -0
  199. package/lib/structures/ViewerNode.js +3 -0
  200. package/lib/structures/ViewerNode.js.map +1 -0
  201. package/lib/structures/ViewerPayload.d.ts +42 -0
  202. package/lib/structures/ViewerPayload.js +3 -0
  203. package/lib/structures/ViewerPayload.js.map +1 -0
  204. package/lib/view.d.ts +9 -0
  205. package/lib/view.js +203 -91
  206. package/lib/view.js.map +1 -1
  207. package/lib/viewer/viewer.js +1 -1
  208. package/package.json +8 -6
  209. package/src/CapturedProcessOutput.ts +50 -0
  210. package/src/ILauncherOption.ts +24 -0
  211. package/src/IProjectOptions.ts +19 -0
  212. package/src/ParsedLauncherOptions.ts +13 -0
  213. package/src/TtscGraphApplication.ts +38 -1
  214. package/src/TtscGraphLauncherArguments.ts +207 -0
  215. package/src/TtscGraphViewSnapshot.ts +46 -0
  216. package/src/index.ts +9 -160
  217. package/src/internal/GraphProcessTrace.ts +242 -0
  218. package/src/launcherArgs.ts +89 -23
  219. package/src/model/IArtifactDirectory.ts +27 -0
  220. package/src/model/IArtifactInputs.ts +21 -0
  221. package/src/model/IPublishedArtifacts.ts +81 -0
  222. package/src/model/IPublishedArtifactsResident.ts +24 -0
  223. package/src/model/ITtscLintDaemonTarget.ts +22 -0
  224. package/src/model/TtscGraphLinePeer.ts +318 -0
  225. package/src/model/TtscGraphMemory.ts +284 -246
  226. package/src/model/TtscGraphNativeArguments.ts +67 -0
  227. package/src/model/TtscGraphNodeId.ts +57 -3
  228. package/src/model/TtscGraphProjection.ts +473 -0
  229. package/src/model/TtscGraphProtocol.ts +90 -0
  230. package/src/model/TtscGraphReadonly.ts +17 -0
  231. package/src/model/TtscGraphRequestOptions.ts +16 -0
  232. package/src/model/TtscGraphSession.ts +196 -475
  233. package/src/model/TtscGraphSessionOptions.ts +22 -0
  234. package/src/model/TtscGraphSessionState.ts +558 -0
  235. package/src/model/TtscGraphShardStore.ts +99 -23
  236. package/src/model/TtscGraphSourceReader.ts +37 -15
  237. package/src/model/TtscLintDaemon.ts +55 -136
  238. package/src/model/TtscLintDaemonState.ts +174 -0
  239. package/src/model/copyGraphRecords.ts +17 -0
  240. package/src/model/copyGraphSnapshot.ts +30 -0
  241. package/src/model/loadGraph.ts +23 -12
  242. package/src/model/publishedArtifacts.ts +361 -182
  243. package/src/nativeExecutable.ts +93 -26
  244. package/src/reduce.ts +24 -51
  245. package/src/resolveGraphBinary.ts +13 -3
  246. package/src/runGraph.ts +90 -0
  247. package/src/server/IResolvedGraphHandle.ts +24 -0
  248. package/src/server/createServer.ts +16 -22
  249. package/src/server/exportSurface.ts +23 -1
  250. package/src/server/formatGraphFailure.ts +32 -0
  251. package/src/server/pathPolicy.ts +70 -5
  252. package/src/server/resolveHandle.ts +45 -14
  253. package/src/server/resultAudit.ts +58 -27
  254. package/src/server/resultNext.ts +26 -1
  255. package/src/server/runDetails.ts +138 -73
  256. package/src/server/runEntrypoints.ts +13 -1
  257. package/src/server/runLookup.ts +23 -7
  258. package/src/server/runOverview.ts +17 -5
  259. package/src/server/runTour.ts +44 -82
  260. package/src/server/runTrace.ts +175 -102
  261. package/src/server/startServer.ts +29 -1
  262. package/src/structures/ITtscGraphApplication.ts +95 -41
  263. package/src/structures/ITtscGraphDecorator.ts +18 -0
  264. package/src/structures/ITtscGraphDetails.ts +81 -6
  265. package/src/structures/ITtscGraphDocTag.ts +17 -8
  266. package/src/structures/ITtscGraphDump.ts +106 -8
  267. package/src/structures/ITtscGraphEdge.ts +14 -2
  268. package/src/structures/ITtscGraphEntrypoints.ts +81 -6
  269. package/src/structures/ITtscGraphEscape.ts +24 -2
  270. package/src/structures/ITtscGraphEvidence.ts +9 -0
  271. package/src/structures/ITtscGraphLookup.ts +42 -9
  272. package/src/structures/ITtscGraphNext.ts +12 -1
  273. package/src/structures/ITtscGraphNode.ts +38 -11
  274. package/src/structures/ITtscGraphOverview.ts +88 -6
  275. package/src/structures/ITtscGraphSnapshot.ts +117 -2
  276. package/src/structures/ITtscGraphSpan.ts +9 -0
  277. package/src/structures/ITtscGraphTour.ts +83 -9
  278. package/src/structures/ITtscGraphTrace.ts +100 -14
  279. package/src/structures/RawDump.ts +25 -0
  280. package/src/structures/RawEdge.ts +22 -0
  281. package/src/structures/RawNode.ts +31 -0
  282. package/src/structures/TtscGraphArtifactNodeKind.ts +9 -0
  283. package/src/structures/TtscGraphDumpEdgeKind.ts +15 -1
  284. package/src/structures/TtscGraphDumpNodeKind.ts +9 -0
  285. package/src/structures/TtscGraphEdgeKind.ts +13 -4
  286. package/src/structures/TtscGraphNodeKind.ts +9 -0
  287. package/src/structures/TtscGraphNodeModifier.ts +9 -0
  288. package/src/structures/ViewerLink.ts +22 -0
  289. package/src/structures/ViewerNode.ts +28 -0
  290. package/src/structures/ViewerPayload.ts +52 -0
  291. package/src/view.ts +208 -110
  292. package/src/viewer/legend.ts +119 -27
@@ -1,13 +1,14 @@
1
1
  import { ITtscGraphDump } from "../structures/ITtscGraphDump";
2
2
  import { ITtscGraphEdge } from "../structures/ITtscGraphEdge";
3
- import { ITtscGraphEvidence } from "../structures/ITtscGraphEvidence";
4
3
  import { ITtscGraphNode } from "../structures/ITtscGraphNode";
5
- import { ITtscGraphSpan } from "../structures/ITtscGraphSpan";
6
- import { isArtifactNodeKind } from "../structures/TtscGraphArtifactNodeKind";
7
4
  import { TtscGraphEdgeKind } from "../structures/TtscGraphEdgeKind";
8
- import { ttscGraphNodeIdPath } from "./TtscGraphNodeId";
5
+ import { TtscGraphProjection } from "./TtscGraphProjection";
6
+ import { type TtscGraphReadonly } from "./TtscGraphReadonly";
9
7
  import { TtscGraphSourceReader } from "./TtscGraphSourceReader";
10
8
 
9
+ type SnapshotNode = TtscGraphReadonly<ITtscGraphNode>;
10
+ type SnapshotEdge = TtscGraphReadonly<ITtscGraphEdge>;
11
+
11
12
  /**
12
13
  * The in-memory resident graph the MCP tools answer from.
13
14
  *
@@ -18,101 +19,254 @@ import { TtscGraphSourceReader } from "./TtscGraphSourceReader";
18
19
  * implementation relationships are checker facts already present in the dump.
19
20
  * Every tool call is then a lookup or traversal over the indexes built here;
20
21
  * nothing recompiles.
22
+ *
23
+ * Snapshot records are owned copies and recursively frozen. Borrowed node
24
+ * records and edge buckets have recursively readonly types, so caller changes
25
+ * cannot invalidate generation-keyed indexes and caches.
26
+ *
27
+ * @evidence contracts/common.md#principled-implementation Native facts are preserved while module/file folding, exact owner suffixes and artifact parents synthesize only this layer's structural relationships.
28
+ * @evidence contracts/common.md#clear-and-simple-design One snapshot owns node, name, relation and citation indexes; individual accessors expose that shared model.
29
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Export surfaces remain checker relationships rather than guessed from local export flags or fixture paths.
30
+ * @evidence contracts/common.md#meaningful-documentation Native paragraphs distinguish compiler facts from synthesized structure, and accessors explain lookup and absence semantics.
31
+ * @evidenceExclude contracts/performance.md#efficient-algorithms from and fromResident own projection/index construction through their private helpers; this class declaration describes the resulting representation.
32
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work from and fromResident establish each shared generation; the declaration does not independently coordinate requests.
33
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources from and fromResident transfer generation storage to their caller; session/caller release owns its duration rather than this declaration.
34
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation an in-memory index over parsed facts; the source reader it owns is the only file reader.
21
35
  */
22
36
  export class TtscGraphMemory {
23
- private readonly byId: Map<string, ITtscGraphNode>;
24
- private readonly outEdges: Map<string, ITtscGraphEdge[]>;
25
- private readonly inEdges: Map<string, ITtscGraphEdge[]>;
26
- private readonly byNameIndex: Map<string, ITtscGraphNode[]>;
27
- private readonly bySymbolIndex: Map<string, ITtscGraphNode[]>;
28
- private readonly byDocTagTarget: Map<string, ITtscGraphNode[]>;
37
+ private readonly nodeKeys: Map<
38
+ SnapshotNode,
39
+ { symbols: readonly string[]; targets: readonly string[] }
40
+ >;
41
+
42
+ private readonly byId: Map<string, SnapshotNode>;
43
+ private readonly outEdges: Map<string, readonly SnapshotEdge[]>;
44
+ private readonly inEdges: Map<string, readonly SnapshotEdge[]>;
45
+ private readonly byNameIndex: Map<string, readonly SnapshotNode[]>;
46
+ private readonly bySymbolIndex: Map<string, readonly SnapshotNode[]>;
47
+ private readonly byDocTagTarget: Map<string, readonly SnapshotNode[]>;
29
48
 
30
49
  /** The absolute project root the dump was built for. */
31
50
  readonly project: string;
51
+
32
52
  /** Every post-fold node, including refined properties and file containers. */
33
- readonly nodes: readonly ITtscGraphNode[];
53
+ readonly nodes: readonly SnapshotNode[];
54
+
34
55
  /** Every edge, raw plus synthesized containment. */
35
- readonly edges: readonly ITtscGraphEdge[];
56
+ readonly edges: readonly SnapshotEdge[];
57
+
36
58
  /** Provenance-gated source display facts cached for this exact snapshot. */
37
59
  readonly source: TtscGraphSourceReader;
38
60
 
39
61
  private constructor(
40
62
  project: string,
41
- nodes: ITtscGraphNode[],
42
- edges: ITtscGraphEdge[],
43
- provenance: ITtscGraphDump.IProvenance,
63
+ nodes: readonly SnapshotNode[],
64
+ edges: readonly SnapshotEdge[],
65
+ provenance:
66
+ | ITtscGraphDump.IProvenance
67
+ | TtscGraphReadonly<ITtscGraphDump.IProvenance>
68
+ | undefined,
69
+ private readonly projection?: TtscGraphProjection.State,
70
+ previous?: TtscGraphMemory,
44
71
  ) {
45
72
  this.project = project;
46
73
  this.nodes = nodes;
47
74
  this.edges = edges;
48
- this.source = new TtscGraphSourceReader(project, provenance);
49
-
50
- this.byId = new Map(nodes.map((n) => [n.id, n]));
51
- this.byNameIndex = new Map();
52
- this.bySymbolIndex = new Map();
53
- this.byDocTagTarget = new Map();
75
+ // Source adjudication belongs to this generation even when graph facts
76
+ // remain equivalent. Never transfer cached lines or cached absence.
77
+ this.source = new TtscGraphSourceReader(
78
+ project,
79
+ provenance === undefined
80
+ ? undefined
81
+ : {
82
+ capabilities: [...provenance.capabilities],
83
+ sources: provenance.sources.map((source) => ({ ...source })),
84
+ },
85
+ );
86
+ this.byId = new Map(nodes.map((node) => [node.id, node]));
87
+ this.nodeKeys = new Map();
54
88
  for (const node of nodes) {
55
- const bucket = this.byNameIndex.get(node.name);
56
- if (bucket) bucket.push(node);
57
- else this.byNameIndex.set(node.name, [node]);
58
- if (node.kind !== "file") {
59
- push(this.bySymbolIndex, node.name, node);
60
- if (node.qualifiedName !== undefined) {
61
- push(this.bySymbolIndex, node.qualifiedName, node);
62
- }
63
- }
64
- for (const target of docTagTargetsOf(node)) {
65
- const carriers = this.byDocTagTarget.get(target);
66
- // The membership check is redundant today — `docTagTargetsOf`
67
- // deduplicates within a node and this loop visits each node once — and
68
- // it is kept because the cost is a scan of a list that holds the
69
- // carriers of one address, while the failure it prevents is a
70
- // declaration reported twice as implementing one specification.
71
- if (carriers === undefined) this.byDocTagTarget.set(target, [node]);
72
- else if (!carriers.includes(node)) carriers.push(node);
73
- }
74
- }
75
- this.outEdges = new Map();
76
- this.inEdges = new Map();
77
- for (const edge of edges) {
78
- push(this.outEdges, edge.from, edge);
79
- push(this.inEdges, edge.to, edge);
89
+ const retained = previous?.nodeKeys.get(node);
90
+ this.nodeKeys.set(
91
+ node,
92
+ retained ?? {
93
+ symbols:
94
+ node.kind === "file"
95
+ ? []
96
+ : node.qualifiedName !== undefined &&
97
+ node.qualifiedName !== node.name
98
+ ? [node.name, node.qualifiedName]
99
+ : [node.name],
100
+ targets: docTagTargetsOf(node),
101
+ },
102
+ );
80
103
  }
104
+ this.byNameIndex = index(nodes, (node) => node.name, previous?.byNameIndex);
105
+ this.bySymbolIndex = index(
106
+ nodes,
107
+ (node) => this.nodeKeys.get(node)!.symbols,
108
+ previous?.bySymbolIndex,
109
+ );
110
+ this.byDocTagTarget = index(
111
+ nodes,
112
+ (node) => this.nodeKeys.get(node)!.targets,
113
+ previous?.byDocTagTarget,
114
+ );
115
+ this.outEdges = index(edges, (edge) => edge.from, previous?.outEdges);
116
+ this.inEdges = index(edges, (edge) => edge.to, previous?.inEdges);
117
+ Object.freeze(this);
81
118
  }
82
119
 
83
- /** Build a model from a parsed dump, synthesizing structural relationships. */
120
+ /**
121
+ * Build a model from a parsed dump, synthesizing structural relationships.
122
+ *
123
+ * The parsed dump must describe one valid generation. Node records are copied
124
+ * before member-kind refinement so the caller's dump is unchanged.
125
+ *
126
+ * @evidence contracts/common.md#principled-implementation Synthesis reanchors native module exports and derives containment from exact owner handles and disambiguating declaration ranges before constructing the indexes for that generation.
127
+ * @evidence contracts/common.md#clear-and-simple-design Synthesis owns representation changes; the private constructor owns index construction.
128
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No export edges are guessed from flags, and artifact ownership follows its producer rather than TypeScript id heuristics.
129
+ * @evidence contracts/common.md#meaningful-documentation Native prose states the valid-generation precondition and caller-dump preservation.
130
+ * @evidence contracts/performance.md#efficient-algorithms Synthesis indexes nodes by file and handle, scanning only colliding owner buckets once per member; owned snapshot copying and index construction scan nodes, edges and facets; per-node target sets make citation deduplication linear in tag population without repeatedly scanning carrier buckets.
131
+ * @evidence contracts/performance.md#reuse-equivalent-work One model builds all indexes once over owned frozen facts and buckets, so external mutation cannot invalidate shared generation identity.
132
+ * @evidence contracts/performance.md#bound-retention-and-release-resources The model retains node/edge arrays, lookup indexes and source cache proportional to its generation; the session or direct caller releases the whole model when no longer needed.
133
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation builds indexes from a parsed dump object and reads no file; source text goes through TtscGraphSourceReader.
134
+ */
84
135
  static from(dump: ITtscGraphDump): TtscGraphMemory {
85
- const { nodes, edges } = synthesize(dump);
136
+ const { nodes, edges } = TtscGraphProjection.full(dump);
86
137
  return new TtscGraphMemory(dump.project, nodes, edges, dump.provenance);
87
138
  }
88
139
 
89
- /** The node with this id, or undefined. */
90
- node(id: string): ITtscGraphNode | undefined {
140
+ /**
141
+ * Project validated frozen resident facts while retaining equivalent work.
142
+ *
143
+ * The shard store invokes this before committing its staged generation. Each
144
+ * model owns new index maps and a new source reader; unchanged facts and
145
+ * ordered index buckets may be shared with the previous immutable model.
146
+ *
147
+ * @evidence contracts/common.md#principled-implementation Validated frozen fact identity and complete projection dependencies qualify component reuse; construction never mutates the preceding model.
148
+ * @evidence contracts/common.md#clear-and-simple-design The projection owner handles synthesis components and this model owns ordered query indexes and source authority.
149
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts This internal path requires the store's complete validation; public from still detaches caller DTOs and no graph facts or validation are omitted.
150
+ * @evidence contracts/common.md#meaningful-documentation Native prose states the validated-input premise, staged commit and per-generation source-reader boundary.
151
+ * @evidence contracts/performance.md#efficient-algorithms Projection scans references and rebuilds only invalidated components; index scans retain equal buckets without allocating their arrays, while maps and generation arrays remain linear in facts.
152
+ * @evidence contracts/performance.md#reuse-equivalent-work Same-file owner/module dependencies and metadata qualify immutable components; exact ordered reference equality qualifies node/relation/citation buckets. New source adjudication is intentionally independent.
153
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Each model retains only its current components, facts, indexes and source reader; unchanged values may also belong to retained old models, without a link retaining prior model generations.
154
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation Processes only frozen in-memory facts; source IO remains with the per-generation reader.
155
+ */
156
+ static fromResident(
157
+ dump: TtscGraphReadonly<ITtscGraphDump>,
158
+ previous?: TtscGraphMemory,
159
+ ): TtscGraphMemory {
160
+ const projection = TtscGraphProjection.create(dump, previous?.projection);
161
+ return new TtscGraphMemory(
162
+ dump.project,
163
+ projection.nodes,
164
+ projection.edges,
165
+ dump.provenance,
166
+ projection,
167
+ previous,
168
+ );
169
+ }
170
+
171
+ /**
172
+ * The node with this exact stable id, or undefined when absent.
173
+ *
174
+ * @evidence contracts/common.md#principled-implementation The id index returns the snapshot's exact node without conflating same-named declarations.
175
+ * @evidence contracts/common.md#clear-and-simple-design This accessor delegates identity lookup to the single model index.
176
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Missing identities are not replaced by name-matched candidates.
177
+ * @evidence contracts/common.md#meaningful-documentation The native headline states exact-id and missing-result semantics.
178
+ * @evidence contracts/performance.md#efficient-algorithms Exact identity requires one construction-time map lookup and no node scan.
179
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work The accessor borrows a completed id index; from owns shared construction.
180
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Returning an existing node acquires no handle or independent retained entry.
181
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation one Map lookup over in-memory facts.
182
+ */
183
+ node(id: string): SnapshotNode | undefined {
91
184
  return this.byId.get(id);
92
185
  }
93
186
 
94
- /** Edges leaving a node (the node is the `from`). */
95
- outgoing(id: string): readonly ITtscGraphEdge[] {
187
+ /**
188
+ * Edges leaving a node (the node is the from endpoint), empty when absent.
189
+ *
190
+ * @evidence contracts/common.md#principled-implementation The outgoing index preserves native and synthesized edge direction for the exact node id.
191
+ * @evidence contracts/common.md#clear-and-simple-design The accessor exposes one readonly bucket without rescanning the edge population.
192
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Absence returns no edges rather than inferred calls from matching names.
193
+ * @evidence contracts/common.md#meaningful-documentation Native prose states endpoint direction, missing behavior and the readonly return contract.
194
+ * @evidence contracts/performance.md#efficient-algorithms A map lookup returns the outgoing bucket without traversing unrelated edges.
195
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work from owns the reused adjacency computation; the accessor only borrows its result.
196
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Existing buckets remain generation-owned; the accessor acquires no separate resource.
197
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation one Map lookup over in-memory edges.
198
+ */
199
+ outgoing(id: string): readonly SnapshotEdge[] {
96
200
  return this.outEdges.get(id) ?? [];
97
201
  }
98
202
 
99
- /** Edges entering a node (the node is the `to`). */
100
- incoming(id: string): readonly ITtscGraphEdge[] {
203
+ /**
204
+ * Edges entering a node (the node is the to endpoint), empty when absent.
205
+ *
206
+ * @evidence contracts/common.md#principled-implementation The incoming index selects edges by their original target identity.
207
+ * @evidence contracts/common.md#clear-and-simple-design The accessor shares construction-time adjacency instead of implementing another traversal.
208
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No callers are guessed from text or substituted when the bucket is absent.
209
+ * @evidence contracts/common.md#meaningful-documentation Native prose states incoming direction and empty absence before the tags.
210
+ * @evidence contracts/performance.md#efficient-algorithms A map lookup returns the incoming bucket without scanning graph edges.
211
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This accessor borrows the index already shared by from rather than coordinating another producer.
212
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources It retains no state beyond the generation's existing edge bucket.
213
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation one Map lookup over in-memory edges.
214
+ */
215
+ incoming(id: string): readonly SnapshotEdge[] {
101
216
  return this.inEdges.get(id) ?? [];
102
217
  }
103
218
 
104
- /** Every node whose simple name equals `name`. */
105
- named(name: string): readonly ITtscGraphNode[] {
219
+ /**
220
+ * Every node whose simple name equals name, empty when absent.
221
+ *
222
+ * @evidence contracts/common.md#principled-implementation The exact simple-name index returns all matching identities, preserving ambiguity.
223
+ * @evidence contracts/common.md#clear-and-simple-design A readonly bucket supplies name lookup independently of ranked search.
224
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Multiple declarations remain candidates rather than selecting the expected fixture's declaration.
225
+ * @evidence contracts/common.md#meaningful-documentation Native prose describes exact matching and empty absence.
226
+ * @evidence contracts/performance.md#efficient-algorithms Exact-name lookup performs one map access instead of filtering all nodes.
227
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Name-index construction and validity belong to from, not this borrowed bucket lookup.
228
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The returned bucket remains generation-owned with no separately retained cache or handle.
229
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation one Map lookup over in-memory facts.
230
+ */
231
+ named(name: string): readonly SnapshotNode[] {
106
232
  return this.byNameIndex.get(name) ?? [];
107
233
  }
108
234
 
109
- /** Every non-file node whose simple or owner-qualified symbol handle matches. */
110
- symbols(handle: string): readonly ITtscGraphNode[] {
235
+ /**
236
+ * Every non-file node whose simple or owner-qualified symbol handle matches.
237
+ *
238
+ * Matches borrow a frozen generation-owned bucket; no match returns an empty
239
+ * readonly list, and several matches remain distinct.
240
+ *
241
+ * @evidence contracts/common.md#principled-implementation The symbol index includes simple and qualified handles but excludes file containers from symbol resolution.
242
+ * @evidence contracts/common.md#clear-and-simple-design One shared index serves all exact-handle consumers without duplicate resolution policy.
243
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The accessor preserves ambiguity rather than fabricating a unique resolution.
244
+ * @evidence contracts/common.md#meaningful-documentation Native prose explains non-file selection, qualified handles and multiple/missing matches.
245
+ * @evidence contracts/performance.md#efficient-algorithms Both simple and qualified handles use the construction-time symbol index, so this accessor performs one map lookup without scanning candidates.
246
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This borrows one already-built symbol bucket; model construction owns shared work.
247
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Existing candidates remain frozen generation-owned records in a borrowed bucket; the accessor acquires no separate resource or retained entry.
248
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation one Map lookup over in-memory facts.
249
+ */
250
+ symbols(handle: string): readonly SnapshotNode[] {
111
251
  return this.bySymbolIndex.get(handle) ?? [];
112
252
  }
113
253
 
114
- /** Every workspace node on its module's export surface. */
115
- exported(): ITtscGraphNode[] {
254
+ /**
255
+ * Every workspace node marked exported by the native declaration facts.
256
+ *
257
+ * External declarations are omitted. Module-specific re-export membership is
258
+ * available separately through exports edges.
259
+ *
260
+ * @evidence contracts/common.md#principled-implementation Filtering exported flags and excluding external nodes yields local exported declarations, not a guessed package front door.
261
+ * @evidence contracts/common.md#clear-and-simple-design The accessor performs one explicit population filter; per-module surface remains in relation indexes.
262
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts The API does not equate declaring-file export flags with all barrel export relationships.
263
+ * @evidence contracts/common.md#meaningful-documentation Native prose distinguishes exported declarations from module-specific re-exports and states external omission.
264
+ * @evidence contracts/performance.md#efficient-algorithms One O(V) filter over generation nodes selects exported nonexternal declarations; no separate export-flag index is built.
265
+ * @evidence contracts/performance.md#reuse-equivalent-work Completed exported-list memoization is not implemented; every call returns a new mutable array over the shared generation nodes.
266
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The projected array belongs to its caller; this method retains no separate state.
267
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation filters the in-memory node array; no file, path or process.
268
+ */
269
+ exported(): SnapshotNode[] {
116
270
  return this.nodes.filter((n) => n.exported && !n.external);
117
271
  }
118
272
 
@@ -131,8 +285,17 @@ export class TtscGraphMemory {
131
285
  * part of a tag's text names a thing belongs to whatever convention wrote the
132
286
  * tag, so this is a selection rule of the consuming layer rather than a fact
133
287
  * the producer claims.
288
+ *
289
+ * @evidence contracts/common.md#principled-implementation Exact target lookup returns indexed declaration carriers under documentationTarget's explicit consumer-side selection rule.
290
+ * @evidence contracts/common.md#clear-and-simple-design The reverse citation index is built once alongside node indexes instead of rescanning tags per query.
291
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts A match identifies a written citation, not a coverage or truth verdict about its target.
292
+ * @evidence contracts/common.md#meaningful-documentation Native paragraphs explain reverse lookup, exact spelling and the consumer heuristic's authority boundary.
293
+ * @evidence contracts/performance.md#efficient-algorithms Reverse citation lookup uses the constructed target index instead of scanning all tags per request.
294
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work The accessor borrows the shared citation index; from owns synthesis and its validity key.
295
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources It returns an existing readonly carrier list and acquires no resource or historical cache.
296
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation one Map lookup over in-memory facts.
134
297
  */
135
- citing(target: string): readonly ITtscGraphNode[] {
298
+ citing(target: string): readonly SnapshotNode[] {
136
299
  return this.byDocTagTarget.get(target) ?? [];
137
300
  }
138
301
  }
@@ -145,14 +308,14 @@ export class TtscGraphMemory {
145
308
  * for it will spell it. Splitting on whitespace alone would key it under
146
309
  * `{@link`, which is every link in the project.
147
310
  */
148
- function docTagTargetsOf(node: ITtscGraphNode): string[] {
311
+ function docTagTargetsOf(node: SnapshotNode): string[] {
149
312
  if (node.docTags === undefined) return [];
150
- const targets: string[] = [];
313
+ const targets = new Set<string>();
151
314
  for (const tag of node.docTags) {
152
315
  const token = documentationTarget(tag.text);
153
- if (token !== undefined && !targets.includes(token)) targets.push(token);
316
+ if (token !== undefined) targets.add(token);
154
317
  }
155
- return targets;
318
+ return [...targets];
156
319
  }
157
320
 
158
321
  /**
@@ -178,6 +341,15 @@ function docTagTargetsOf(node: ITtscGraphNode): string[] {
178
341
  * layer whose audit already declares its selection heuristic. A convention
179
342
  * whose addresses look like prose is simply not indexed; nothing is lost from
180
343
  * the tag itself, which `details` still returns in full.
344
+ *
345
+ * @evidence contracts/common.md#principled-implementation Leading-token extraction plus interior separators selects address-like tokens while rejecting ordinary prose; this is explicitly a lookup heuristic.
346
+ * @evidence contracts/common.md#clear-and-simple-design One selector owns citation indexing and query normalization, leaving raw tags unchanged in declaration facts.
347
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Address punctuation is a general convention boundary rather than an allowlist of expected specifications.
348
+ * @evidence contracts/common.md#meaningful-documentation Native paragraphs state false-prose matches, accepted address forms and the limitation for prose-like conventions.
349
+ * @evidence contracts/performance.md#efficient-algorithms Leading-token extraction and address classification scan only the tag text, with no graph-wide lookup.
350
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This pure token classifier has no completed-work coordinator; from builds the reusable citation index.
351
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The optional returned string owns no native resource or retained history.
352
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation tokenizes a tag string; the address it returns is text that is never resolved or opened.
181
353
  */
182
354
  export function documentationTarget(
183
355
  text: string | undefined,
@@ -199,6 +371,15 @@ export function documentationTarget(
199
371
  * An unclosed brace group is not a token: `{@link ISale` with the brace
200
372
  * forgotten would otherwise fall through to the whitespace split and index the
201
373
  * address `{@link`, which every link in the project shares.
374
+ *
375
+ * @evidence contracts/common.md#principled-implementation Trim and first whitespace preserve the opening token; a leading brace group is returned whole only when closed.
376
+ * @evidence contracts/common.md#clear-and-simple-design This lexical helper is shared by citation selection rather than parsing tag semantics itself.
377
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Malformed brace groups return absence instead of being coerced into a ubiquitous partial link.
378
+ * @evidence contracts/common.md#meaningful-documentation Native prose explains brace grouping and the malformed-token reason before acknowledgment tags.
379
+ * @evidence contracts/performance.md#efficient-algorithms A single bounded text scan finds the token boundary without constructing a parser or scanning unrelated tags.
380
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This pure per-string extraction does not coordinate reusable producer work.
381
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Only the returned token survives the call; no retained map or handle is acquired.
382
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation splits a string at whitespace or a brace group; the token is never resolved or opened.
202
383
  */
203
384
  export function leadingToken(text: string | undefined): string | undefined {
204
385
  const trimmed = text?.trim();
@@ -211,192 +392,49 @@ export function leadingToken(text: string | undefined): string | undefined {
211
392
  return stop < 0 ? trimmed : trimmed.slice(0, stop);
212
393
  }
213
394
 
214
- /** Append value to the slice stored at key, creating the slice on first use. */
215
- function push<K, V>(map: Map<K, V[]>, key: K, value: V): void {
216
- const bucket = map.get(key);
217
- if (bucket) bucket.push(value);
218
- else map.set(key, [value]);
219
- }
220
-
221
- /**
222
- * The within-file identity of a node: its owner-qualified name when it has one
223
- * (`Class.method`), else its simple name. Two nodes in one file never share a
224
- * key, so it is the handle the ownership synthesis looks owners up by.
225
- */
226
- function keyOf(node: ITtscGraphNode): string {
227
- return node.qualifiedName ?? node.name;
228
- }
229
-
230
395
  /**
231
- * The owner key derived from facts the producer serialized separately.
396
+ * Build an ordered index, copying a bucket only after its membership differs.
232
397
  *
233
- * A quoted member named `"a.b"` has Name `a.b` and QualifiedName `Box.a.b`.
234
- * Cutting the qualified name at its last dot invents owner `Box.a`; removing
235
- * the exact `.${name}` suffix instead preserves the producer's real boundary.
236
- */
237
- function ownerKey(node: ITtscGraphNode): string | undefined {
238
- if (node.qualifiedName === undefined || node.qualifiedName === node.name)
239
- return undefined;
240
- const suffix = `.${node.name}`;
241
- if (!node.qualifiedName.endsWith(suffix)) return undefined;
242
- const owner = node.qualifiedName.slice(0, -suffix.length);
243
- return owner === "" ? undefined : owner;
244
- }
245
-
246
- /** A file's id and node name from its dump path coordinate. */
247
- function fileNodeId(file: string): string {
248
- return file;
249
- }
250
-
251
- /**
252
- * A wire span with its file put back: the one the builder left out because the
253
- * reader has it, or the one it kept because it could not be derived (an
254
- * implementation in another file).
398
+ * The scan compares reference identity at each position with the previous
399
+ * immutable bucket. Prefixes are copied only on the first difference; a
400
+ * shortened bucket is copied at the end. New maps never mutate old maps or
401
+ * frozen buckets. Cost is linear in memberships plus changed bucket sizes;
402
+ * current counts/maps are temporary and no historical index is retained.
255
403
  */
256
- function spanIn(span: ITtscGraphSpan, file: string): ITtscGraphEvidence {
257
- return { ...span, file: span.file ?? file };
258
- }
259
-
260
- /**
261
- * The source file a node id names. An id is `path#Qualified.Name:kind`, and a
262
- * file node's id is the path itself.
263
- */
264
- function fileOfNodeId(id: string): string {
265
- return ttscGraphNodeIdPath(id) ?? id;
266
- }
267
-
268
- function basename(file: string): string {
269
- const slash = file.lastIndexOf("/");
270
- return slash >= 0 ? file.slice(slash + 1) : file;
271
- }
272
-
273
- /**
274
- * Derive the structural layer from a dump's faithful facts: refine class-member
275
- * variables to properties, add a `file` node per workspace source, connect the
276
- * `contains` ownership tree, and re-anchor compiler-owned `exports` edges.
277
- */
278
- function synthesize(dump: ITtscGraphDump): {
279
- nodes: ITtscGraphNode[];
280
- edges: ITtscGraphEdge[];
281
- } {
282
- // A module node is the dump's name for a source file's export surface, and a
283
- // file node is this layer's name for the same file. Fold the two: the module
284
- // keeps its file present here even when the file declares nothing (a barrel),
285
- // and its `exports` edges are re-anchored on the file id every other tool
286
- // already traverses. What the module carried, the file now carries.
287
- const moduleFiles = new Set(
288
- dump.nodes.filter((n) => n.kind === "module").map((n) => n.file),
289
- );
290
- const moduleIds = new Map(
291
- dump.nodes.filter((n) => n.kind === "module").map((n) => [n.id, n.file]),
292
- );
293
- // Clone nodes so property refinement does not mutate the caller's dump, and
294
- // put back the file the builder left out of every span: a node's span is in
295
- // the node's file, an edge's span is in the file its `from` id names. The
296
- // builder omits both because they are exactly reconstructible and they are not
297
- // small — the two copies are 17% of the document, 55 MB of VS Code's 323 MB,
298
- // paid again in the encode, the pipe, the parse and the validation. Nothing
299
- // downstream of this line sees a span without its file.
300
- const nodes: ITtscGraphNode[] = dump.nodes.flatMap((n): ITtscGraphNode[] => {
301
- if (n.kind === "module") return [];
302
- const { evidence, implementation, ...rest } = n;
303
- return [
304
- {
305
- ...rest,
306
- kind: n.kind,
307
- ...(evidence !== undefined
308
- ? { evidence: spanIn(evidence, n.file) }
309
- : {}),
310
- ...(implementation !== undefined
311
- ? { implementation: spanIn(implementation, n.file) }
312
- : {}),
313
- },
314
- ];
315
- });
316
- const edges: ITtscGraphEdge[] = dump.edges.map((edge) => {
317
- const { evidence, ...rest } = edge;
318
- const from = moduleIds.get(edge.from);
319
- return {
320
- ...rest,
321
- ...(from !== undefined ? { from: fileNodeId(from) } : {}),
322
- ...(evidence !== undefined
323
- ? { evidence: spanIn(evidence, fileOfNodeId(edge.from)) }
324
- : {}),
325
- };
326
- });
327
-
328
- // Index workspace nodes by (file, within-file key) so ownership can resolve a
329
- // member to its declaring class/namespace.
330
- const byFileKey = new Map<string, ITtscGraphNode>();
331
- for (const node of nodes) {
332
- if (!node.external) byFileKey.set(node.file + "\0" + keyOf(node), node);
333
- }
334
- const owner = (node: ITtscGraphNode): ITtscGraphNode | undefined => {
335
- const parent = ownerKey(node);
336
- if (parent === undefined) return undefined;
337
- return byFileKey.get(node.file + "\0" + parent);
338
- };
339
-
340
- // Refine: a `variable` whose owner is a class or interface is a property.
341
- for (const node of nodes) {
342
- if (node.kind !== "variable" || node.external) continue;
343
- const parent = owner(node);
344
- if (parent && (parent.kind === "class" || parent.kind === "interface")) {
345
- node.kind = "property";
404
+ function index<T>(
405
+ values: readonly T[],
406
+ keys: (value: T) => string | readonly string[],
407
+ previous?: ReadonlyMap<string, readonly T[]>,
408
+ ): Map<string, readonly T[]> {
409
+ const result = new Map<string, readonly T[]>();
410
+ const counts = new Map<string, number>();
411
+ const append = (key: string, value: T): void => {
412
+ const count = counts.get(key) ?? 0;
413
+ let bucket = result.get(key);
414
+ if (bucket === undefined) {
415
+ bucket = previous?.get(key) ?? [];
416
+ result.set(key, bucket);
346
417
  }
347
- }
348
-
349
- // One file container node per distinct workspace source file, plus every file
350
- // the dump saw an export surface on — a barrel declares nothing, so its only
351
- // trace in the dump is its module node, and it is exactly the file a consumer
352
- // imports the package from.
353
- const fileNodes = new Map<string, ITtscGraphNode>();
354
- const addFileNode = (file: string): void => {
355
- if (file === "" || fileNodes.has(file)) return;
356
- fileNodes.set(file, {
357
- id: fileNodeId(file),
358
- kind: "file",
359
- name: basename(file),
360
- file,
361
- external: false,
362
- });
418
+ if (bucket === previous?.get(key)) {
419
+ if (bucket[count] !== value) {
420
+ bucket = [...bucket.slice(0, count), value];
421
+ result.set(key, bucket);
422
+ }
423
+ } else (bucket as T[]).push(value);
424
+ counts.set(key, count + 1);
363
425
  };
364
- for (const node of nodes) {
365
- if (node.external || isArtifactNodeKind(node.kind)) continue;
366
- addFileNode(node.file);
367
- }
368
- for (const file of moduleFiles) addFileNode(file);
369
-
370
- // Ownership: a member is contained by its owner; a top-level declaration by
371
- // its file. Exports are not synthesized here: the dump's `exports` edges come
372
- // from the checker's export table, which follows re-exports and barrels, so
373
- // they say which module puts a symbol on the wire. Deriving them from the
374
- // `exported` flag instead would say only that the declaring file made it
375
- // public, which is the fact that cannot tell a package's front door from its
376
- // legacy subpath.
377
- const structural: ITtscGraphEdge[] = [];
378
- for (const node of nodes) {
379
- // An artifact is contained by the artifact its producer named, and by
380
- // nothing when that producer named none. It is never contained by a `file`
381
- // node: a document is already its own node, a Prisma address carries no
382
- // path on purpose, and an API operation has no file at all.
383
- if (isArtifactNodeKind(node.kind)) {
384
- if (node.parent !== undefined && node.parent !== "")
385
- structural.push({ from: node.parent, to: node.id, kind: "contains" });
386
- continue;
426
+ for (const value of values) {
427
+ const selected = keys(value);
428
+ if (typeof selected === "string") append(selected, value);
429
+ else {
430
+ for (const key of selected) append(key, value);
387
431
  }
388
- if (node.external || node.file === "") continue;
389
- const parent = owner(node);
390
- const container = parent ? parent.id : fileNodeId(node.file);
391
- structural.push({
392
- from: container,
393
- to: node.id,
394
- kind: "contains",
395
- });
396
432
  }
397
-
398
- return {
399
- nodes: [...nodes, ...fileNodes.values()],
400
- edges: [...edges, ...structural],
401
- };
433
+ for (const [key, bucket] of result) {
434
+ const count = counts.get(key)!;
435
+ if (bucket === previous?.get(key) && bucket.length !== count)
436
+ result.set(key, Object.freeze(bucket.slice(0, count)));
437
+ else if (bucket !== previous?.get(key)) Object.freeze(bucket);
438
+ }
439
+ return result;
402
440
  }