@ttsc/graph 0.30.3 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -30
- package/lib/CapturedProcessOutput.d.ts +47 -0
- package/lib/CapturedProcessOutput.js +3 -0
- package/lib/CapturedProcessOutput.js.map +1 -0
- package/lib/ILauncherOption.d.ts +22 -0
- package/lib/ILauncherOption.js +3 -0
- package/lib/ILauncherOption.js.map +1 -0
- package/lib/IProjectOptions.d.ts +18 -0
- package/lib/IProjectOptions.js +3 -0
- package/lib/IProjectOptions.js.map +1 -0
- package/lib/ParsedLauncherOptions.d.ts +13 -0
- package/lib/ParsedLauncherOptions.js +3 -0
- package/lib/ParsedLauncherOptions.js.map +1 -0
- package/lib/TtscGraphApplication.d.ts +38 -1
- package/lib/TtscGraphApplication.js +26 -1
- package/lib/TtscGraphApplication.js.map +1 -1
- package/lib/TtscGraphLauncherArguments.d.ts +115 -0
- package/lib/TtscGraphLauncherArguments.js +181 -0
- package/lib/TtscGraphLauncherArguments.js.map +1 -0
- package/lib/TtscGraphViewSnapshot.d.ts +35 -0
- package/lib/TtscGraphViewSnapshot.js +48 -0
- package/lib/TtscGraphViewSnapshot.js.map +1 -0
- package/lib/index.d.ts +9 -24
- package/lib/index.js +6 -128
- package/lib/index.js.map +1 -1
- package/lib/internal/GraphProcessTrace.d.ts +1 -0
- package/lib/internal/GraphProcessTrace.js +235 -0
- package/lib/internal/GraphProcessTrace.js.map +1 -0
- package/lib/launcherArgs.d.ts +75 -16
- package/lib/launcherArgs.js +77 -8
- package/lib/launcherArgs.js.map +1 -1
- package/lib/model/IArtifactDirectory.d.ts +26 -0
- package/lib/model/IArtifactDirectory.js +3 -0
- package/lib/model/IArtifactDirectory.js.map +1 -0
- package/lib/model/IArtifactInputs.d.ts +19 -0
- package/lib/model/IArtifactInputs.js +3 -0
- package/lib/model/IArtifactInputs.js.map +1 -0
- package/lib/model/IPublishedArtifacts.d.ts +76 -0
- package/lib/model/IPublishedArtifacts.js +3 -0
- package/lib/model/IPublishedArtifacts.js.map +1 -0
- package/lib/model/IPublishedArtifactsResident.d.ts +19 -0
- package/lib/model/IPublishedArtifactsResident.js +3 -0
- package/lib/model/IPublishedArtifactsResident.js.map +1 -0
- package/lib/model/ITtscLintDaemonTarget.d.ts +20 -0
- package/lib/model/ITtscLintDaemonTarget.js +3 -0
- package/lib/model/ITtscLintDaemonTarget.js.map +1 -0
- package/lib/model/TtscGraphLinePeer.d.ts +146 -0
- package/lib/model/TtscGraphLinePeer.js +213 -0
- package/lib/model/TtscGraphLinePeer.js.map +1 -0
- package/lib/model/TtscGraphMemory.d.ts +156 -16
- package/lib/model/TtscGraphMemory.js +228 -222
- package/lib/model/TtscGraphMemory.js.map +1 -1
- package/lib/model/TtscGraphNativeArguments.d.ts +40 -0
- package/lib/model/TtscGraphNativeArguments.js +64 -0
- package/lib/model/TtscGraphNativeArguments.js.map +1 -0
- package/lib/model/TtscGraphNodeId.d.ts +55 -3
- package/lib/model/TtscGraphNodeId.js +40 -2
- package/lib/model/TtscGraphNodeId.js.map +1 -1
- package/lib/model/TtscGraphProjection.d.ts +156 -0
- package/lib/model/TtscGraphProjection.js +390 -0
- package/lib/model/TtscGraphProjection.js.map +1 -0
- package/lib/model/TtscGraphProtocol.d.ts +28 -0
- package/lib/model/TtscGraphProtocol.js +928 -0
- package/lib/model/TtscGraphProtocol.js.map +1 -0
- package/lib/model/TtscGraphReadonly.d.ts +15 -0
- package/lib/model/TtscGraphReadonly.js +3 -0
- package/lib/model/TtscGraphReadonly.js.map +1 -0
- package/lib/model/TtscGraphRequestOptions.d.ts +16 -0
- package/lib/model/TtscGraphRequestOptions.js +3 -0
- package/lib/model/TtscGraphRequestOptions.js.map +1 -0
- package/lib/model/TtscGraphSession.d.ts +82 -36
- package/lib/model/TtscGraphSession.js +164 -1238
- package/lib/model/TtscGraphSession.js.map +1 -1
- package/lib/model/TtscGraphSessionOptions.d.ts +20 -0
- package/lib/model/TtscGraphSessionOptions.js +3 -0
- package/lib/model/TtscGraphSessionOptions.js.map +1 -0
- package/lib/model/TtscGraphSessionState.d.ts +187 -0
- package/lib/model/TtscGraphSessionState.js +417 -0
- package/lib/model/TtscGraphSessionState.js.map +1 -0
- package/lib/model/TtscGraphShardStore.d.ts +64 -3
- package/lib/model/TtscGraphShardStore.js +85 -15
- package/lib/model/TtscGraphShardStore.js.map +1 -1
- package/lib/model/TtscGraphSourceReader.d.ts +31 -8
- package/lib/model/TtscGraphSourceReader.js +37 -13
- package/lib/model/TtscGraphSourceReader.js.map +1 -1
- package/lib/model/TtscLintDaemon.d.ts +35 -17
- package/lib/model/TtscLintDaemon.js +39 -117
- package/lib/model/TtscLintDaemon.js.map +1 -1
- package/lib/model/TtscLintDaemonState.d.ts +69 -0
- package/lib/model/TtscLintDaemonState.js +166 -0
- package/lib/model/TtscLintDaemonState.js.map +1 -0
- package/lib/model/copyGraphRecords.d.ts +14 -0
- package/lib/model/copyGraphRecords.js +19 -0
- package/lib/model/copyGraphRecords.js.map +1 -0
- package/lib/model/copyGraphSnapshot.d.ts +17 -0
- package/lib/model/copyGraphSnapshot.js +33 -0
- package/lib/model/copyGraphSnapshot.js.map +1 -0
- package/lib/model/loadGraph.d.ts +19 -10
- package/lib/model/loadGraph.js +21 -12
- package/lib/model/loadGraph.js.map +1 -1
- package/lib/model/publishedArtifacts.d.ts +78 -97
- package/lib/model/publishedArtifacts.js +271 -85
- package/lib/model/publishedArtifacts.js.map +1 -1
- package/lib/nativeExecutable.d.ts +19 -8
- package/lib/nativeExecutable.js +75 -15
- package/lib/nativeExecutable.js.map +1 -1
- package/lib/reduce.d.ts +19 -45
- package/lib/reduce.js +19 -3
- package/lib/reduce.js.map +1 -1
- package/lib/resolveGraphBinary.d.ts +9 -0
- package/lib/resolveGraphBinary.js +11 -3
- package/lib/resolveGraphBinary.js.map +1 -1
- package/lib/runGraph.d.ts +21 -0
- package/lib/runGraph.js +83 -0
- package/lib/runGraph.js.map +1 -0
- package/lib/server/IResolvedGraphHandle.d.ts +22 -0
- package/lib/server/IResolvedGraphHandle.js +3 -0
- package/lib/server/IResolvedGraphHandle.js.map +1 -0
- package/lib/server/createServer.d.ts +14 -10
- package/lib/server/createServer.js +42 -39
- package/lib/server/createServer.js.map +1 -1
- package/lib/server/exportSurface.d.ts +23 -1
- package/lib/server/exportSurface.js +23 -1
- package/lib/server/exportSurface.js.map +1 -1
- package/lib/server/formatGraphFailure.d.ts +20 -0
- package/lib/server/formatGraphFailure.js +35 -0
- package/lib/server/formatGraphFailure.js.map +1 -0
- package/lib/server/pathPolicy.d.ts +70 -5
- package/lib/server/pathPolicy.js +66 -4
- package/lib/server/pathPolicy.js.map +1 -1
- package/lib/server/resolveHandle.d.ts +10 -5
- package/lib/server/resolveHandle.js +39 -7
- package/lib/server/resolveHandle.js.map +1 -1
- package/lib/server/resultAudit.d.ts +17 -14
- package/lib/server/resultAudit.js +56 -27
- package/lib/server/resultAudit.js.map +1 -1
- package/lib/server/resultNext.d.ts +26 -1
- package/lib/server/resultNext.js +14 -0
- package/lib/server/resultNext.js.map +1 -1
- package/lib/server/runDetails.d.ts +72 -15
- package/lib/server/runDetails.js +104 -69
- package/lib/server/runDetails.js.map +1 -1
- package/lib/server/runEntrypoints.d.ts +9 -0
- package/lib/server/runEntrypoints.js +9 -0
- package/lib/server/runEntrypoints.js.map +1 -1
- package/lib/server/runLookup.d.ts +13 -3
- package/lib/server/runLookup.js +19 -6
- package/lib/server/runLookup.js.map +1 -1
- package/lib/server/runOverview.d.ts +13 -4
- package/lib/server/runOverview.js +13 -4
- package/lib/server/runOverview.js.map +1 -1
- package/lib/server/runTour.d.ts +9 -0
- package/lib/server/runTour.js +33 -65
- package/lib/server/runTour.js.map +1 -1
- package/lib/server/runTrace.d.ts +25 -1
- package/lib/server/runTrace.js +133 -88
- package/lib/server/runTrace.js.map +1 -1
- package/lib/server/startServer.d.ts +16 -0
- package/lib/server/startServer.js +24 -1
- package/lib/server/startServer.js.map +1 -1
- package/lib/structures/ITtscGraphApplication.d.ts +95 -41
- package/lib/structures/ITtscGraphDecorator.d.ts +18 -0
- package/lib/structures/ITtscGraphDetails.d.ts +81 -6
- package/lib/structures/ITtscGraphDocTag.d.ts +17 -8
- package/lib/structures/ITtscGraphDump.d.ts +106 -8
- package/lib/structures/ITtscGraphEdge.d.ts +14 -2
- package/lib/structures/ITtscGraphEntrypoints.d.ts +81 -6
- package/lib/structures/ITtscGraphEscape.d.ts +24 -2
- package/lib/structures/ITtscGraphEvidence.d.ts +9 -0
- package/lib/structures/ITtscGraphLookup.d.ts +42 -9
- package/lib/structures/ITtscGraphNext.d.ts +12 -1
- package/lib/structures/ITtscGraphNode.d.ts +38 -11
- package/lib/structures/ITtscGraphOverview.d.ts +81 -6
- package/lib/structures/ITtscGraphSnapshot.d.ts +97 -2
- package/lib/structures/ITtscGraphSpan.d.ts +9 -0
- package/lib/structures/ITtscGraphTour.d.ts +83 -9
- package/lib/structures/ITtscGraphTrace.d.ts +99 -14
- package/lib/structures/RawDump.d.ts +22 -0
- package/lib/structures/RawDump.js +3 -0
- package/lib/structures/RawDump.js.map +1 -0
- package/lib/structures/RawEdge.d.ts +20 -0
- package/lib/structures/RawEdge.js +3 -0
- package/lib/structures/RawEdge.js.map +1 -0
- package/lib/structures/RawNode.d.ts +26 -0
- package/lib/structures/RawNode.js +3 -0
- package/lib/structures/RawNode.js.map +1 -0
- package/lib/structures/TtscGraphArtifactNodeKind.d.ts +9 -0
- package/lib/structures/TtscGraphArtifactNodeKind.js +9 -0
- package/lib/structures/TtscGraphArtifactNodeKind.js.map +1 -1
- package/lib/structures/TtscGraphDumpEdgeKind.d.ts +15 -1
- package/lib/structures/TtscGraphDumpNodeKind.d.ts +9 -0
- package/lib/structures/TtscGraphEdgeKind.d.ts +13 -4
- package/lib/structures/TtscGraphNodeKind.d.ts +9 -0
- package/lib/structures/TtscGraphNodeModifier.d.ts +9 -0
- package/lib/structures/ViewerLink.d.ts +20 -0
- package/lib/structures/ViewerLink.js +3 -0
- package/lib/structures/ViewerLink.js.map +1 -0
- package/lib/structures/ViewerNode.d.ts +24 -0
- package/lib/structures/ViewerNode.js +3 -0
- package/lib/structures/ViewerNode.js.map +1 -0
- package/lib/structures/ViewerPayload.d.ts +42 -0
- package/lib/structures/ViewerPayload.js +3 -0
- package/lib/structures/ViewerPayload.js.map +1 -0
- package/lib/view.d.ts +9 -0
- package/lib/view.js +203 -91
- package/lib/view.js.map +1 -1
- package/lib/viewer/viewer.js +1 -1
- package/package.json +8 -6
- package/src/CapturedProcessOutput.ts +50 -0
- package/src/ILauncherOption.ts +24 -0
- package/src/IProjectOptions.ts +19 -0
- package/src/ParsedLauncherOptions.ts +13 -0
- package/src/TtscGraphApplication.ts +38 -1
- package/src/TtscGraphLauncherArguments.ts +207 -0
- package/src/TtscGraphViewSnapshot.ts +46 -0
- package/src/index.ts +9 -160
- package/src/internal/GraphProcessTrace.ts +242 -0
- package/src/launcherArgs.ts +89 -23
- package/src/model/IArtifactDirectory.ts +27 -0
- package/src/model/IArtifactInputs.ts +21 -0
- package/src/model/IPublishedArtifacts.ts +81 -0
- package/src/model/IPublishedArtifactsResident.ts +24 -0
- package/src/model/ITtscLintDaemonTarget.ts +22 -0
- package/src/model/TtscGraphLinePeer.ts +318 -0
- package/src/model/TtscGraphMemory.ts +284 -246
- package/src/model/TtscGraphNativeArguments.ts +67 -0
- package/src/model/TtscGraphNodeId.ts +57 -3
- package/src/model/TtscGraphProjection.ts +473 -0
- package/src/model/TtscGraphProtocol.ts +90 -0
- package/src/model/TtscGraphReadonly.ts +17 -0
- package/src/model/TtscGraphRequestOptions.ts +16 -0
- package/src/model/TtscGraphSession.ts +196 -475
- package/src/model/TtscGraphSessionOptions.ts +22 -0
- package/src/model/TtscGraphSessionState.ts +558 -0
- package/src/model/TtscGraphShardStore.ts +99 -23
- package/src/model/TtscGraphSourceReader.ts +37 -15
- package/src/model/TtscLintDaemon.ts +55 -136
- package/src/model/TtscLintDaemonState.ts +174 -0
- package/src/model/copyGraphRecords.ts +17 -0
- package/src/model/copyGraphSnapshot.ts +30 -0
- package/src/model/loadGraph.ts +23 -12
- package/src/model/publishedArtifacts.ts +361 -182
- package/src/nativeExecutable.ts +93 -26
- package/src/reduce.ts +24 -51
- package/src/resolveGraphBinary.ts +13 -3
- package/src/runGraph.ts +90 -0
- package/src/server/IResolvedGraphHandle.ts +24 -0
- package/src/server/createServer.ts +16 -22
- package/src/server/exportSurface.ts +23 -1
- package/src/server/formatGraphFailure.ts +32 -0
- package/src/server/pathPolicy.ts +70 -5
- package/src/server/resolveHandle.ts +45 -14
- package/src/server/resultAudit.ts +58 -27
- package/src/server/resultNext.ts +26 -1
- package/src/server/runDetails.ts +138 -73
- package/src/server/runEntrypoints.ts +13 -1
- package/src/server/runLookup.ts +23 -7
- package/src/server/runOverview.ts +17 -5
- package/src/server/runTour.ts +44 -82
- package/src/server/runTrace.ts +175 -102
- package/src/server/startServer.ts +29 -1
- package/src/structures/ITtscGraphApplication.ts +95 -41
- package/src/structures/ITtscGraphDecorator.ts +18 -0
- package/src/structures/ITtscGraphDetails.ts +81 -6
- package/src/structures/ITtscGraphDocTag.ts +17 -8
- package/src/structures/ITtscGraphDump.ts +106 -8
- package/src/structures/ITtscGraphEdge.ts +14 -2
- package/src/structures/ITtscGraphEntrypoints.ts +81 -6
- package/src/structures/ITtscGraphEscape.ts +24 -2
- package/src/structures/ITtscGraphEvidence.ts +9 -0
- package/src/structures/ITtscGraphLookup.ts +42 -9
- package/src/structures/ITtscGraphNext.ts +12 -1
- package/src/structures/ITtscGraphNode.ts +38 -11
- package/src/structures/ITtscGraphOverview.ts +88 -6
- package/src/structures/ITtscGraphSnapshot.ts +117 -2
- package/src/structures/ITtscGraphSpan.ts +9 -0
- package/src/structures/ITtscGraphTour.ts +83 -9
- package/src/structures/ITtscGraphTrace.ts +100 -14
- package/src/structures/RawDump.ts +25 -0
- package/src/structures/RawEdge.ts +22 -0
- package/src/structures/RawNode.ts +31 -0
- package/src/structures/TtscGraphArtifactNodeKind.ts +9 -0
- package/src/structures/TtscGraphDumpEdgeKind.ts +15 -1
- package/src/structures/TtscGraphDumpNodeKind.ts +9 -0
- package/src/structures/TtscGraphEdgeKind.ts +13 -4
- package/src/structures/TtscGraphNodeKind.ts +9 -0
- package/src/structures/TtscGraphNodeModifier.ts +9 -0
- package/src/structures/ViewerLink.ts +22 -0
- package/src/structures/ViewerNode.ts +28 -0
- package/src/structures/ViewerPayload.ts +52 -0
- package/src/view.ts +208 -110
- package/src/viewer/legend.ts +119 -27
package/src/server/runTrace.ts
CHANGED
|
@@ -1,29 +1,21 @@
|
|
|
1
1
|
import { TtscGraphMemory } from "../model/TtscGraphMemory";
|
|
2
|
+
import { TtscGraphReadonly } from "../model/TtscGraphReadonly";
|
|
2
3
|
import { ITtscGraphEdge } from "../structures/ITtscGraphEdge";
|
|
3
4
|
import { ITtscGraphEvidence } from "../structures/ITtscGraphEvidence";
|
|
4
|
-
import { ITtscGraphNode } from "../structures/ITtscGraphNode";
|
|
5
|
+
import { ITtscGraphNode as NodeShape } from "../structures/ITtscGraphNode";
|
|
5
6
|
import { ITtscGraphTrace } from "../structures/ITtscGraphTrace";
|
|
6
7
|
import { isDeclarationFile, isExternalNode, isTestPath } from "./pathPolicy";
|
|
7
8
|
import { resolveGraphHandle } from "./resolveHandle";
|
|
8
9
|
import { IRunnerOutput, resultNext } from "./resultNext";
|
|
9
10
|
import { edgeEvidenceOf, signatureOf } from "./runDetails";
|
|
10
11
|
|
|
12
|
+
type ITtscGraphNode = TtscGraphReadonly<NodeShape>;
|
|
13
|
+
|
|
11
14
|
const DEFAULT_DEPTH = 3;
|
|
12
15
|
const DEFAULT_MAX_NODES = 12;
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
// hop, so the model re-issued a trace per hop and paid a round trip for each: a
|
|
17
|
-
// single vue question spent twenty-six calls walking a flow the graph could have
|
|
18
|
-
// walked once. The cap now follows the chain instead of cutting it.
|
|
19
|
-
const MAX_OPEN_DEPTH = 8;
|
|
20
|
-
const MAX_OPEN_NODES = 32;
|
|
21
|
-
const MAX_IMPACT_DEPTH = 4;
|
|
22
|
-
const MAX_IMPACT_NODES = 16;
|
|
23
|
-
// Path mode walks further than an open trace because it is looking for one
|
|
24
|
-
// named end, not building a picture. The cap is the one the request contract
|
|
25
|
-
// publishes for path mode.
|
|
26
|
-
const MAX_PATH_DEPTH = 12;
|
|
16
|
+
// Exploratory defaults are bounded; explicit budgets have no upper clamp.
|
|
17
|
+
// Path searches default deeper because they seek one named target.
|
|
18
|
+
const DEFAULT_PATH_DEPTH = 12;
|
|
27
19
|
const MAX_HOPS_PER_NODE = 2;
|
|
28
20
|
const MAX_STEPS = 12;
|
|
29
21
|
const DISPATCH_KINDS = new Set<string>(["overrides", "implements"]);
|
|
@@ -33,14 +25,8 @@ const BODYLESS_KINDS = new Set<string>(["interface", "type"]);
|
|
|
33
25
|
// `abstract` and `declare` are the two keywords that take the body away from a
|
|
34
26
|
// declaration that would otherwise have to have one.
|
|
35
27
|
const BODYLESS_MODIFIERS = new Set<string>(["abstract", "declare"]);
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// dump of the codebase rather than an answer. Past this many, the declaration
|
|
39
|
-
// stays a leaf and `details` answers `implementedBy` for a caller that wants the
|
|
40
|
-
// list. The cut is the graph's existing definition of a hub (see
|
|
41
|
-
// `isSharedUtility`): across the benchmark corpus it follows 84–100% of dispatch
|
|
42
|
-
// sites per project and refuses only the genuinely polymorphic ones (zod's
|
|
43
|
-
// 36-way schema interface, VS Code's 533-way disposable).
|
|
28
|
+
// Bounded exploratory requests leave broad polymorphic fanouts unexpanded;
|
|
29
|
+
// complete mode follows every declared concrete implementation.
|
|
44
30
|
const DISPATCH_HUB = 12;
|
|
45
31
|
|
|
46
32
|
/**
|
|
@@ -48,6 +34,18 @@ const DISPATCH_HUB = 12;
|
|
|
48
34
|
* edges are excluded so the path is real call/type flow; forward walks callees,
|
|
49
35
|
* reverse and impact walk callers. Impact additionally tags each reached node's
|
|
50
36
|
* role so the blast radius on the public surface is legible.
|
|
37
|
+
*
|
|
38
|
+
* Complete requests exhaust the finite admitted graph, or find one shortest
|
|
39
|
+
* requested path, without traversal budgets.
|
|
40
|
+
*
|
|
41
|
+
* @evidence contracts/common.md#principled-implementation Breadth-first traversal preserves shortest reached depth and original edge direction; path search distinguishes found, depth-bounded, dispatch-fanout-withheld and exhausted outcomes before considering shared junctions, so neither bound is reported as an absence.
|
|
42
|
+
* @evidence contracts/common.md#clear-and-simple-design Handle resolution, eligible edges, dispatch, path search and coordinate summaries have helper owners; this function assembles open or requested-path results.
|
|
43
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Ambiguity remains candidates and dispatch follows checker implementation relations; shared references are never inserted as guessed execution edges.
|
|
44
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains direction, structural exclusion and impact roles; request documentation states bounds and focus meanings.
|
|
45
|
+
* @evidence contracts/performance.md#efficient-algorithms A visited set expands each node once through indexed adjacency. Complete and path requests may inspect the admitted graph; ordering edges costs up to O(E log E), with additional ownership walks for declaration-body facts. Per-base dispatch and target indexes avoid rebuilding or rescanning the same fanout for each reverse arrival. Path reconstruction is linear in path length, and temporary/output space grows with inspected nodes and edges.
|
|
46
|
+
* @evidence contracts/performance.md#reuse-equivalent-work Forward/reverse operations share immutable generation indexes and resolution helpers. A request-local map reuses bodyless dispatch selection for a base across reverse arrivals; its focus and complete policy remain constant for that request. Trace results remain fresh mutable outputs.
|
|
47
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources Visited/frontier sets and omitted dispatch candidates exist only during the request; defaults bound selected output, not every inspected frontier edge. Complete requests retain up to the admitted graph nodes and edges until return, without a fixed response-size bound; allocation or transport failures propagate instead of yielding partial success.
|
|
48
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation walks in-memory edges; no file, path or process.
|
|
51
49
|
*/
|
|
52
50
|
export function runTrace(
|
|
53
51
|
graph: TtscGraphMemory,
|
|
@@ -55,20 +53,13 @@ export function runTrace(
|
|
|
55
53
|
): IRunnerOutput<ITtscGraphTrace> {
|
|
56
54
|
const direction = props.direction ?? "forward";
|
|
57
55
|
const focus = props.focus ?? "all";
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const maxNodes = bound(
|
|
66
|
-
props.maxNodes,
|
|
67
|
-
DEFAULT_MAX_NODES,
|
|
68
|
-
1,
|
|
69
|
-
impact ? MAX_IMPACT_NODES : MAX_OPEN_NODES,
|
|
70
|
-
);
|
|
71
|
-
const maxHops = maxNodes * MAX_HOPS_PER_NODE;
|
|
56
|
+
const complete = props.complete === true;
|
|
57
|
+
const dispatch = new Map<string, IDispatchSelection>();
|
|
58
|
+
const maxDepth = complete ? Infinity : bound(props.maxDepth, DEFAULT_DEPTH);
|
|
59
|
+
const maxNodes = complete
|
|
60
|
+
? Infinity
|
|
61
|
+
: bound(props.maxNodes, DEFAULT_MAX_NODES);
|
|
62
|
+
const maxHops = complete ? Infinity : maxNodes * MAX_HOPS_PER_NODE;
|
|
72
63
|
const reverse = direction === "reverse" || direction === "impact";
|
|
73
64
|
const includeExternal = props.includeExternal === true;
|
|
74
65
|
// Only an impact trace tags reached nodes with their public-surface role; for
|
|
@@ -146,7 +137,9 @@ export function runTrace(
|
|
|
146
137
|
),
|
|
147
138
|
};
|
|
148
139
|
}
|
|
149
|
-
const pathDepth =
|
|
140
|
+
const pathDepth = complete
|
|
141
|
+
? Infinity
|
|
142
|
+
: bound(props.maxDepth, DEFAULT_PATH_DEPTH);
|
|
150
143
|
const search = findPath(
|
|
151
144
|
graph,
|
|
152
145
|
start.node.id,
|
|
@@ -154,6 +147,8 @@ export function runTrace(
|
|
|
154
147
|
pathDepth,
|
|
155
148
|
focus,
|
|
156
149
|
includeExternal,
|
|
150
|
+
complete,
|
|
151
|
+
dispatch,
|
|
157
152
|
);
|
|
158
153
|
const hasPath = search.found !== undefined;
|
|
159
154
|
const path = search.found?.path ?? [];
|
|
@@ -163,7 +158,7 @@ export function runTrace(
|
|
|
163
158
|
// presenting a shared symbol as "the seam" when a direct call path may run
|
|
164
159
|
// past the bound sends the caller to a seam that is not there.
|
|
165
160
|
const junctions =
|
|
166
|
-
hasPath || search.bounded
|
|
161
|
+
hasPath || search.bounded || search.withheld
|
|
167
162
|
? []
|
|
168
163
|
: junctionsBetween(graph, start.node.id, target.node.id, focus);
|
|
169
164
|
return {
|
|
@@ -173,7 +168,8 @@ export function runTrace(
|
|
|
173
168
|
target: summary(graph, target.node),
|
|
174
169
|
hops,
|
|
175
170
|
path: path.map((node, i) => summary(graph, node, i, false, true)),
|
|
176
|
-
steps: traceSteps(graph, hops),
|
|
171
|
+
steps: traceSteps(graph, hops, complete),
|
|
172
|
+
truncated: !hasPath && (search.bounded || search.withheld),
|
|
177
173
|
...(junctions.length > 0 ? { junctions } : {}),
|
|
178
174
|
},
|
|
179
175
|
// A missing path is a fact, not an answer, and the old message called it
|
|
@@ -193,16 +189,18 @@ export function runTrace(
|
|
|
193
189
|
? pathNext
|
|
194
190
|
: search.bounded
|
|
195
191
|
? resultNext("inspect", boundedPathReason(pathDepth), "trace")
|
|
196
|
-
:
|
|
197
|
-
? resultNext(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
192
|
+
: search.withheld
|
|
193
|
+
? resultNext("inspect", withheldPathReason(), "details")
|
|
194
|
+
: junctions.length > 0
|
|
195
|
+
? resultNext(
|
|
196
|
+
"inspect",
|
|
197
|
+
"No call path runs between the two ends — a callback stands between them (an event emitter, a subscription, a lifecycle hook), and no call edge crosses one. `junctions` names the symbols both ends touch, which is the seam: trace the junction to see who registers on it and who fires it.",
|
|
198
|
+
"trace",
|
|
199
|
+
)
|
|
200
|
+
: resultNext(
|
|
201
|
+
"outside",
|
|
202
|
+
"No call path runs from the start to the target and they touch nothing in common, so the graph holds no connection between them.",
|
|
203
|
+
),
|
|
206
204
|
};
|
|
207
205
|
}
|
|
208
206
|
|
|
@@ -222,6 +220,8 @@ export function runTrace(
|
|
|
222
220
|
id,
|
|
223
221
|
reverse,
|
|
224
222
|
focus,
|
|
223
|
+
complete,
|
|
224
|
+
dispatch,
|
|
225
225
|
);
|
|
226
226
|
// A hop the hub bound withheld is omitted whatever the depth budget does
|
|
227
227
|
// next, so the flag is set before the boundary check consumes the step.
|
|
@@ -310,7 +310,7 @@ export function runTrace(
|
|
|
310
310
|
direction,
|
|
311
311
|
hops,
|
|
312
312
|
reached: [...reached.values()],
|
|
313
|
-
steps: traceSteps(graph, hops),
|
|
313
|
+
steps: traceSteps(graph, hops, complete),
|
|
314
314
|
truncated,
|
|
315
315
|
},
|
|
316
316
|
next: resultNext(
|
|
@@ -339,11 +339,13 @@ function traceEdges(
|
|
|
339
339
|
id: string,
|
|
340
340
|
reverse: boolean,
|
|
341
341
|
focus: ITtscGraphTrace.IRequest["focus"],
|
|
342
|
+
complete: boolean,
|
|
343
|
+
dispatch: Map<string, IDispatchSelection>,
|
|
342
344
|
): { edges: readonly ITtscGraphEdge[]; omitted: readonly ITtscGraphEdge[] } {
|
|
343
345
|
const edges = reverse ? graph.incoming(id) : graph.outgoing(id);
|
|
344
346
|
const dispatched = reverse
|
|
345
|
-
? reverseDispatchEdges(graph, id, focus)
|
|
346
|
-
: dispatchEdges(graph, id, focus);
|
|
347
|
+
? reverseDispatchEdges(graph, id, focus, complete, dispatch)
|
|
348
|
+
: dispatchEdges(graph, id, focus, complete, dispatch);
|
|
347
349
|
// Nothing to add is the common case, and a walk visits the nodes with the
|
|
348
350
|
// largest edge lists, so hand back the stored list rather than a copy of it.
|
|
349
351
|
return {
|
|
@@ -359,6 +361,9 @@ function traceEdges(
|
|
|
359
361
|
interface IDispatchSelection {
|
|
360
362
|
readonly selected: readonly ITtscGraphEdge[];
|
|
361
363
|
readonly omitted: readonly ITtscGraphEdge[];
|
|
364
|
+
|
|
365
|
+
/** Reuses each fanout edge for reverse arrivals without rescanning siblings. */
|
|
366
|
+
readonly byTarget?: ReadonlyMap<string, ITtscGraphEdge>;
|
|
362
367
|
}
|
|
363
368
|
|
|
364
369
|
// Returned by every no-dispatch path rather than allocated per call, which is
|
|
@@ -389,8 +394,9 @@ function eligibleTraceEndpoint(
|
|
|
389
394
|
function traceSteps(
|
|
390
395
|
graph: TtscGraphMemory,
|
|
391
396
|
hops: ITtscGraphTrace.IHop[],
|
|
397
|
+
complete: boolean,
|
|
392
398
|
): string[] {
|
|
393
|
-
return hops.slice(0, MAX_STEPS).map((hop) => {
|
|
399
|
+
return (complete ? hops : hops.slice(0, MAX_STEPS)).map((hop) => {
|
|
394
400
|
const from = graph.node(hop.from);
|
|
395
401
|
const to = graph.node(hop.to);
|
|
396
402
|
const lhs = from?.qualifiedName ?? from?.name ?? hop.from;
|
|
@@ -476,7 +482,9 @@ function touchedBy(
|
|
|
476
482
|
touched.set(edge.to, {
|
|
477
483
|
kind: edge.kind,
|
|
478
484
|
outgoing: true,
|
|
479
|
-
...(edge.evidence !== undefined
|
|
485
|
+
...(edge.evidence !== undefined
|
|
486
|
+
? { evidence: { ...edge.evidence } }
|
|
487
|
+
: {}),
|
|
480
488
|
});
|
|
481
489
|
}
|
|
482
490
|
for (const edge of graph.incoming(id)) {
|
|
@@ -485,32 +493,37 @@ function touchedBy(
|
|
|
485
493
|
touched.set(edge.from, {
|
|
486
494
|
kind: edge.kind,
|
|
487
495
|
outgoing: false,
|
|
488
|
-
...(edge.evidence !== undefined
|
|
496
|
+
...(edge.evidence !== undefined
|
|
497
|
+
? { evidence: { ...edge.evidence } }
|
|
498
|
+
: {}),
|
|
489
499
|
});
|
|
490
500
|
}
|
|
491
501
|
return touched;
|
|
492
502
|
}
|
|
493
503
|
|
|
494
|
-
/**
|
|
495
|
-
* What to say when the depth bound, not the graph, ended a path search.
|
|
496
|
-
*
|
|
497
|
-
* The caller asked a bounded question and the old answer returned a claim about
|
|
498
|
-
* the whole graph — "they touch nothing in common, so the graph holds no
|
|
499
|
-
* connection between them" — which is the worst thing an index can say wrongly:
|
|
500
|
-
* the caller stops asking and either reads files or concludes the dependency is
|
|
501
|
-
* not there. So report the boundary, and make the continuation one the caller
|
|
502
|
-
* can actually take. At the 12-hop ceiling there is no larger `maxDepth` to
|
|
503
|
-
* retry with, and a message that only invites one would be a dead end of its
|
|
504
|
-
* own; two bounded walks from opposite ends cover twice the distance and are
|
|
505
|
-
* requests the tool already answers.
|
|
506
|
-
*/
|
|
504
|
+
/** Explain an incomplete path search and its supported complete retry. */
|
|
507
505
|
function boundedPathReason(depth: number): string {
|
|
508
506
|
return (
|
|
509
507
|
`No path was found within the requested depth of ${depth}, but the walk stopped on that bound with eligible graph still ahead of it. ` +
|
|
510
508
|
`This is a boundary, not an absence: the two ends may be connected further out, and nothing here says they are not. ` +
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
509
|
+
`Re-run with a larger \`maxDepth\` or \`complete: true\` to search the entire admitted graph.`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* What to say when a dispatch fanout past the hub cut, not the graph, ended a
|
|
515
|
+
* path search.
|
|
516
|
+
*
|
|
517
|
+
* A declaration with this many implementations stays a leaf in a trace, so the
|
|
518
|
+
* walk never entered them. Telling the caller the ends touch nothing in common
|
|
519
|
+
* would turn that policy into a claim about the codebase, and the caller would
|
|
520
|
+
* stop asking while the connection ran through one of those implementations.
|
|
521
|
+
*/
|
|
522
|
+
function withheldPathReason(): string {
|
|
523
|
+
return (
|
|
524
|
+
`No path was found, but the walk did not follow a dispatch fanout of ${DISPATCH_HUB} or more implementations, so it did not exhaust the graph. ` +
|
|
525
|
+
`This is a boundary of the walk, not an absence: the ends may connect through one of those implementations. ` +
|
|
526
|
+
`Re-run with \`complete: true\` to follow the entire dispatch fanout, or ask \`details\` for the implementation that the question means.`
|
|
514
527
|
);
|
|
515
528
|
}
|
|
516
529
|
|
|
@@ -529,6 +542,13 @@ interface IPathSearch {
|
|
|
529
542
|
* still ahead of it, so nothing was proven about the two ends.
|
|
530
543
|
*/
|
|
531
544
|
bounded: boolean;
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* True when no path was found and the walk left a dispatch fanout past the
|
|
548
|
+
* hub cut unfollowed, reaching an implementation no other route visited, so
|
|
549
|
+
* the walk exhausted only the graph it was willing to follow.
|
|
550
|
+
*/
|
|
551
|
+
withheld: boolean;
|
|
532
552
|
}
|
|
533
553
|
|
|
534
554
|
/**
|
|
@@ -549,11 +569,17 @@ function findPath(
|
|
|
549
569
|
maxDepth: number,
|
|
550
570
|
focus: ITtscGraphTrace.IRequest["focus"],
|
|
551
571
|
includeExternal: boolean,
|
|
572
|
+
complete: boolean,
|
|
573
|
+
dispatch: Map<string, IDispatchSelection>,
|
|
552
574
|
): IPathSearch {
|
|
553
575
|
const startNode = graph.node(startId);
|
|
554
|
-
if (startNode === undefined) return { bounded: false };
|
|
576
|
+
if (startNode === undefined) return { bounded: false, withheld: false };
|
|
555
577
|
if (startId === targetId)
|
|
556
|
-
return {
|
|
578
|
+
return {
|
|
579
|
+
found: { path: [startNode], hops: [] },
|
|
580
|
+
bounded: false,
|
|
581
|
+
withheld: false,
|
|
582
|
+
};
|
|
557
583
|
const parent = new Map<
|
|
558
584
|
string,
|
|
559
585
|
{
|
|
@@ -564,13 +590,35 @@ function findPath(
|
|
|
564
590
|
>();
|
|
565
591
|
const visited = new Set<string>([startId]);
|
|
566
592
|
let bounded = false;
|
|
593
|
+
const withheld = new Set<string>();
|
|
567
594
|
let queue: Array<{ id: string; depth: number }> = [{ id: startId, depth: 0 }];
|
|
568
595
|
while (queue.length > 0) {
|
|
569
596
|
const next: Array<{ id: string; depth: number }> = [];
|
|
570
597
|
for (const { id, depth } of queue) {
|
|
571
598
|
// The forward step the open trace would take, built in one place so the
|
|
572
599
|
// two walks cannot disagree about what a step follows.
|
|
573
|
-
const { edges: candidates } = traceEdges(
|
|
600
|
+
const { edges: candidates, omitted } = traceEdges(
|
|
601
|
+
graph,
|
|
602
|
+
id,
|
|
603
|
+
false,
|
|
604
|
+
focus,
|
|
605
|
+
complete,
|
|
606
|
+
dispatch,
|
|
607
|
+
);
|
|
608
|
+
// A dispatch fanout past the hub cut is not walked, but each hop in it is
|
|
609
|
+
// a real, eligible continuation. Leaving it out of the search while still
|
|
610
|
+
// reporting "no path" would claim an absence the walk never established,
|
|
611
|
+
// unless the walk reaches that implementation some other way.
|
|
612
|
+
for (const edge of omitted) {
|
|
613
|
+
const endpoint = eligibleTraceEndpoint(
|
|
614
|
+
graph,
|
|
615
|
+
edge,
|
|
616
|
+
false,
|
|
617
|
+
focus,
|
|
618
|
+
includeExternal,
|
|
619
|
+
);
|
|
620
|
+
if (endpoint !== undefined) withheld.add(endpoint.otherId);
|
|
621
|
+
}
|
|
574
622
|
if (depth >= maxDepth) {
|
|
575
623
|
if (
|
|
576
624
|
candidates.some(
|
|
@@ -605,9 +653,10 @@ function findPath(
|
|
|
605
653
|
while (cur !== startId) {
|
|
606
654
|
const p = parent.get(cur);
|
|
607
655
|
if (p === undefined) break;
|
|
608
|
-
ids.
|
|
656
|
+
ids.push(p.via);
|
|
609
657
|
cur = p.via;
|
|
610
658
|
}
|
|
659
|
+
ids.reverse();
|
|
611
660
|
const path: ITtscGraphNode[] = [];
|
|
612
661
|
for (const nid of ids) {
|
|
613
662
|
const n = graph.node(nid);
|
|
@@ -624,17 +673,17 @@ function findPath(
|
|
|
624
673
|
depth: i,
|
|
625
674
|
};
|
|
626
675
|
if (parentEdge?.evidence !== undefined)
|
|
627
|
-
hop.evidence = parentEdge.evidence;
|
|
676
|
+
hop.evidence = { ...parentEdge.evidence };
|
|
628
677
|
hops.push(hop);
|
|
629
678
|
}
|
|
630
|
-
return { found: { path, hops }, bounded };
|
|
679
|
+
return { found: { path, hops }, bounded, withheld: false };
|
|
631
680
|
}
|
|
632
681
|
next.push({ id: otherId, depth: depth + 1 });
|
|
633
682
|
}
|
|
634
683
|
}
|
|
635
684
|
queue = next;
|
|
636
685
|
}
|
|
637
|
-
return { bounded };
|
|
686
|
+
return { bounded, withheld: [...withheld].some((id) => !visited.has(id)) };
|
|
638
687
|
}
|
|
639
688
|
|
|
640
689
|
/**
|
|
@@ -783,8 +832,12 @@ function dispatchEdges(
|
|
|
783
832
|
graph: TtscGraphMemory,
|
|
784
833
|
id: string,
|
|
785
834
|
focus: ITtscGraphTrace.IRequest["focus"],
|
|
835
|
+
complete: boolean,
|
|
836
|
+
dispatch: Map<string, IDispatchSelection>,
|
|
786
837
|
): IDispatchSelection {
|
|
787
838
|
if (focus === "types") return NO_DISPATCH;
|
|
839
|
+
const cached = dispatch.get(id);
|
|
840
|
+
if (cached !== undefined) return cached;
|
|
788
841
|
// The checker relations first: almost no node has one, and reading a
|
|
789
842
|
// declaration's own facts walks its ownership chain, which is work worth
|
|
790
843
|
// doing only where there is something to dispatch to.
|
|
@@ -792,10 +845,15 @@ function dispatchEdges(
|
|
|
792
845
|
for (const edge of graph.incoming(id)) {
|
|
793
846
|
if (DISPATCH_KINDS.has(edge.kind)) (relations ??= []).push(edge);
|
|
794
847
|
}
|
|
795
|
-
if (relations === undefined)
|
|
848
|
+
if (relations === undefined) {
|
|
849
|
+
dispatch.set(id, NO_DISPATCH);
|
|
850
|
+
return NO_DISPATCH;
|
|
851
|
+
}
|
|
796
852
|
const declaration = graph.node(id);
|
|
797
|
-
if (declaration === undefined || hasDeclarationBody(graph, declaration))
|
|
853
|
+
if (declaration === undefined || hasDeclarationBody(graph, declaration)) {
|
|
854
|
+
dispatch.set(id, NO_DISPATCH);
|
|
798
855
|
return NO_DISPATCH;
|
|
856
|
+
}
|
|
799
857
|
const out: ITtscGraphEdge[] = [];
|
|
800
858
|
// Per implementation, not per relation. A class may name one base in two
|
|
801
859
|
// heritage clauses — `class Impl extends Base implements Base` is legal — and
|
|
@@ -817,15 +875,20 @@ function dispatchEdges(
|
|
|
817
875
|
from: id,
|
|
818
876
|
to: edge.from,
|
|
819
877
|
kind: "dispatches",
|
|
820
|
-
...(edge.evidence !== undefined
|
|
878
|
+
...(edge.evidence !== undefined
|
|
879
|
+
? { evidence: { ...edge.evidence } }
|
|
880
|
+
: {}),
|
|
821
881
|
});
|
|
822
882
|
}
|
|
823
|
-
//
|
|
824
|
-
//
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
883
|
+
// Bounded walks withhold broad fanouts, retaining omitted edges for truthful
|
|
884
|
+
// truncation. Complete walks follow them without the exploratory hub cut.
|
|
885
|
+
const byTarget = new Map(out.map((edge) => [edge.to, edge]));
|
|
886
|
+
const result =
|
|
887
|
+
!complete && out.length >= DISPATCH_HUB
|
|
888
|
+
? { selected: [], omitted: out, byTarget }
|
|
889
|
+
: { selected: out, omitted: [], byTarget };
|
|
890
|
+
dispatch.set(id, result);
|
|
891
|
+
return result;
|
|
829
892
|
}
|
|
830
893
|
|
|
831
894
|
/**
|
|
@@ -848,6 +911,8 @@ function reverseDispatchEdges(
|
|
|
848
911
|
graph: TtscGraphMemory,
|
|
849
912
|
id: string,
|
|
850
913
|
focus: ITtscGraphTrace.IRequest["focus"],
|
|
914
|
+
complete: boolean,
|
|
915
|
+
dispatch: Map<string, IDispatchSelection>,
|
|
851
916
|
): IDispatchSelection {
|
|
852
917
|
if (focus === "types") return NO_DISPATCH;
|
|
853
918
|
const selected: ITtscGraphEdge[] = [];
|
|
@@ -856,13 +921,13 @@ function reverseDispatchEdges(
|
|
|
856
921
|
for (const edge of graph.outgoing(id)) {
|
|
857
922
|
if (!DISPATCH_KINDS.has(edge.kind) || bases.has(edge.to)) continue;
|
|
858
923
|
bases.add(edge.to);
|
|
859
|
-
const fanout = dispatchEdges(graph, edge.to, focus);
|
|
860
|
-
|
|
861
|
-
|
|
924
|
+
const fanout = dispatchEdges(graph, edge.to, focus, complete, dispatch);
|
|
925
|
+
const match = fanout.byTarget?.get(id);
|
|
926
|
+
if (match === undefined) continue;
|
|
862
927
|
// The base's fanout is bounded as a whole, so a reverse walk that lands on
|
|
863
928
|
// a suppressed sibling has the same omission to report as the forward one.
|
|
864
|
-
|
|
865
|
-
|
|
929
|
+
if (fanout.omitted.length > 0) omitted.push(match);
|
|
930
|
+
else selected.push(match);
|
|
866
931
|
}
|
|
867
932
|
return { selected, omitted };
|
|
868
933
|
}
|
|
@@ -886,6 +951,15 @@ function reverseDispatchEdges(
|
|
|
886
951
|
* keyword is written; and an external leaf has a body the graph deliberately
|
|
887
952
|
* does not hold. Everything else is a concrete declaration, which is a real
|
|
888
953
|
* destination and is never promoted through an override, whatever it calls.
|
|
954
|
+
*
|
|
955
|
+
* @evidence contracts/common.md#principled-implementation Kind, modifiers, declaration-file semantics and containing type facts identify bodyless declarations before dispatch traversal.
|
|
956
|
+
* @evidence contracts/common.md#clear-and-simple-design One shared predicate supplies the body decision to forward and reverse dispatch helpers.
|
|
957
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Body presence is not guessed solely from a function-like name or a missing explicit declare keyword.
|
|
958
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains ambient and containing-type cases and distinguishes a possible body from execution proof.
|
|
959
|
+
* @evidence contracts/performance.md#efficient-algorithms Kind and modifier checks are constant or modifier-count work, followed by indexed containing-owner traversal rather than a graph-wide scan.
|
|
960
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work This shared declaration predicate coordinates no completed or in-flight work; trace and centrality own their producer reuse.
|
|
961
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The predicate borrows graph facts and acquires no handle, task or retained cache.
|
|
962
|
+
* @evidenceExclude contracts/portability.md#os-neutral-implementation reads kind, modifier and ownership facts of in-memory nodes; no file, path or process.
|
|
889
963
|
*/
|
|
890
964
|
export function hasDeclarationBody(
|
|
891
965
|
graph: TtscGraphMemory,
|
|
@@ -906,7 +980,8 @@ function hasBodylessModifier(node: ITtscGraphNode): boolean {
|
|
|
906
980
|
* Whether an owner up the `contains` tree makes this declaration bodyless: an
|
|
907
981
|
* interface, or an ambient container. A member writes no keyword of its own —
|
|
908
982
|
* `declare` on a class or namespace is not repeated on what it holds — so the
|
|
909
|
-
* fact lives on the owner and the walk has to go get it.
|
|
983
|
+
* fact lives on the owner and the walk has to go get it. An abstract class can
|
|
984
|
+
* contain concrete bodies; its abstract modifier applies only to itself.
|
|
910
985
|
*/
|
|
911
986
|
function inBodylessContainer(
|
|
912
987
|
graph: TtscGraphMemory,
|
|
@@ -919,7 +994,10 @@ function inBodylessContainer(
|
|
|
919
994
|
if (container === undefined || container.kind === "file") return false;
|
|
920
995
|
if (seen.has(container.id)) return false;
|
|
921
996
|
seen.add(container.id);
|
|
922
|
-
if (
|
|
997
|
+
if (
|
|
998
|
+
container.kind === "interface" ||
|
|
999
|
+
container.modifiers?.includes("declare")
|
|
1000
|
+
)
|
|
923
1001
|
return true;
|
|
924
1002
|
current = container;
|
|
925
1003
|
}
|
|
@@ -997,12 +1075,7 @@ function evidenceRank(edge: ITtscGraphEdge): number {
|
|
|
997
1075
|
return line * 100 + col;
|
|
998
1076
|
}
|
|
999
1077
|
|
|
1000
|
-
function bound(
|
|
1001
|
-
value: number | undefined,
|
|
1002
|
-
fallback: number,
|
|
1003
|
-
min: number,
|
|
1004
|
-
max: number,
|
|
1005
|
-
): number {
|
|
1078
|
+
function bound(value: number | undefined, fallback: number): number {
|
|
1006
1079
|
const n = value === undefined || !Number.isFinite(value) ? fallback : value;
|
|
1007
|
-
return Math.max(
|
|
1080
|
+
return Math.max(1, Math.floor(n));
|
|
1008
1081
|
}
|
|
@@ -2,16 +2,35 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
2
2
|
|
|
3
3
|
import { TtscGraphSession } from "../model/TtscGraphSession";
|
|
4
4
|
import { createServer } from "./createServer";
|
|
5
|
+
import { formatGraphFailure } from "./formatGraphFailure";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Serve the graph tools over MCP on stdio. The server answers the MCP handshake
|
|
8
9
|
* immediately and opens the resident incremental graph session on the first
|
|
9
10
|
* real tool call, so a large project cannot make the client give up before
|
|
10
11
|
* tools are advertised and an escape request still performs no graph work.
|
|
12
|
+
*
|
|
13
|
+
* The transport and stdin end events close any lazily created native session.
|
|
14
|
+
*
|
|
15
|
+
* @evidence contracts/common.md#principled-implementation A lazy provider opens the resident graph on demand while the stdio MCP transport advertises tools independently of index creation.
|
|
16
|
+
* @evidence contracts/common.md#clear-and-simple-design This boundary owns transport wiring and session lifetime; createServer owns reflected tools and the session owns native snapshots.
|
|
17
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts An escape request does not trigger a forced graph build or a fabricated empty graph response.
|
|
18
|
+
* @evidence contracts/common.md#meaningful-documentation Native prose explains lazy indexing and shutdown ownership; option comments specify project coordinates and handshake version.
|
|
19
|
+
* @evidence contracts/portability.md#os-neutral-implementation The stdio transport and native session abstracts process handling across hosts; cwd and tsconfig are passed as project coordinates without shell interpolation.
|
|
20
|
+
* @evidence contracts/performance.md#efficient-algorithms Startup registers one tool and transport without scanning the project; index work occurs only when the provider is requested.
|
|
21
|
+
* @evidence contracts/performance.md#reuse-equivalent-work One resident session is shared by all tool calls for the selected project and refreshes its generation before use.
|
|
22
|
+
* @evidence contracts/performance.md#bound-retention-and-release-resources The server owns at most one session; transport close or stdin end joins its child processes and pending requests; shutdown failure retains its nonzero exit and nested passive diagnostic.
|
|
11
23
|
*/
|
|
12
24
|
export async function startServer(options: {
|
|
25
|
+
/** Project working directory, defaulting to the process directory. */
|
|
13
26
|
cwd?: string;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Configuration path interpreted relative to cwd, defaulting to
|
|
30
|
+
* tsconfig.json.
|
|
31
|
+
*/
|
|
14
32
|
tsconfig?: string;
|
|
33
|
+
|
|
15
34
|
/** Server version reported in the MCP handshake. */
|
|
16
35
|
version: string;
|
|
17
36
|
}): Promise<void> {
|
|
@@ -19,11 +38,20 @@ export async function startServer(options: {
|
|
|
19
38
|
const tsconfig = options.tsconfig ?? "tsconfig.json";
|
|
20
39
|
let session: TtscGraphSession | undefined;
|
|
21
40
|
const server = createServer(async () => {
|
|
41
|
+
if (closed) throw new Error("@ttsc/graph: server is closed");
|
|
22
42
|
session ??= new TtscGraphSession({ cwd, tsconfig });
|
|
23
43
|
return session.graph();
|
|
24
44
|
}, options.version);
|
|
25
45
|
const transport = new StdioServerTransport();
|
|
26
|
-
|
|
46
|
+
let closed = false;
|
|
47
|
+
const closeSession = () => {
|
|
48
|
+
if (closed) return;
|
|
49
|
+
closed = true;
|
|
50
|
+
void session?.close().catch((error: unknown) => {
|
|
51
|
+
process.stderr.write(`@ttsc/graph: ${formatGraphFailure(error)}\n`);
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
});
|
|
54
|
+
};
|
|
27
55
|
transport.onclose = closeSession;
|
|
28
56
|
process.stdin.once("end", closeSession);
|
|
29
57
|
await server.connect(transport);
|