@ttsc/graph 0.22.0 → 0.24.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 +1 -1
- package/lib/index.js +36 -0
- package/lib/index.js.map +1 -1
- package/lib/model/TtscGraphMemory.d.ts +1 -1
- package/lib/model/TtscGraphMemory.js +16 -13
- package/lib/model/TtscGraphMemory.js.map +1 -1
- package/lib/model/TtscGraphSession.d.ts +2 -0
- package/lib/model/TtscGraphSession.js +414 -220
- package/lib/model/TtscGraphSession.js.map +1 -1
- package/lib/model/TtscGraphShardStore.d.ts +16 -0
- package/lib/model/TtscGraphShardStore.js +272 -0
- package/lib/model/TtscGraphShardStore.js.map +1 -0
- package/lib/model/loadGraph.d.ts +15 -0
- package/lib/model/loadGraph.js +574 -574
- package/lib/model/loadGraph.js.map +1 -1
- package/lib/reduce.js +27 -15
- package/lib/reduce.js.map +1 -1
- package/lib/server/runDetails.js +4 -9
- package/lib/server/runDetails.js.map +1 -1
- package/lib/server/runEntrypoints.js +4 -7
- package/lib/server/runEntrypoints.js.map +1 -1
- package/lib/server/runLookup.js +1 -1
- package/lib/server/runLookup.js.map +1 -1
- package/lib/server/runOverview.js +3 -3
- package/lib/server/runOverview.js.map +1 -1
- package/lib/server/runTour.js +7 -11
- package/lib/server/runTour.js.map +1 -1
- package/lib/server/runTrace.d.ts +4 -5
- package/lib/server/runTrace.js +10 -19
- package/lib/server/runTrace.js.map +1 -1
- package/lib/structures/ITtscGraphDump.d.ts +8 -2
- package/lib/structures/ITtscGraphNode.d.ts +1 -2
- package/lib/structures/ITtscGraphSnapshot.d.ts +37 -1
- package/lib/structures/TtscGraphDumpEdgeKind.d.ts +2 -0
- package/lib/structures/TtscGraphDumpEdgeKind.js +3 -0
- package/lib/structures/TtscGraphDumpEdgeKind.js.map +1 -0
- package/lib/structures/TtscGraphDumpNodeKind.d.ts +2 -0
- package/lib/structures/TtscGraphDumpNodeKind.js +3 -0
- package/lib/structures/TtscGraphDumpNodeKind.js.map +1 -0
- package/lib/structures/TtscGraphEdgeKind.d.ts +8 -6
- package/lib/structures/TtscGraphNodeKind.d.ts +5 -6
- package/lib/structures/TtscGraphNodeModifier.d.ts +1 -1
- package/lib/structures/index.d.ts +2 -0
- package/lib/structures/index.js +2 -0
- package/lib/structures/index.js.map +1 -1
- package/lib/view.js +26 -3
- package/lib/view.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +40 -0
- package/src/model/TtscGraphMemory.ts +10 -8
- package/src/model/TtscGraphSession.ts +64 -16
- package/src/model/TtscGraphShardStore.ts +371 -0
- package/src/model/loadGraph.ts +11 -2
- package/src/reduce.ts +37 -18
- package/src/server/runDetails.ts +5 -10
- package/src/server/runEntrypoints.ts +4 -7
- package/src/server/runLookup.ts +1 -1
- package/src/server/runOverview.ts +3 -3
- package/src/server/runTour.ts +8 -14
- package/src/server/runTrace.ts +10 -19
- package/src/structures/ITtscGraphDump.ts +10 -2
- package/src/structures/ITtscGraphNode.ts +1 -2
- package/src/structures/ITtscGraphSnapshot.ts +42 -1
- package/src/structures/TtscGraphDumpEdgeKind.ts +11 -0
- package/src/structures/TtscGraphDumpNodeKind.ts +10 -0
- package/src/structures/TtscGraphEdgeKind.ts +8 -9
- package/src/structures/TtscGraphNodeKind.ts +5 -11
- package/src/structures/TtscGraphNodeModifier.ts +1 -2
- package/src/structures/index.ts +2 -0
- package/src/view.ts +29 -5
package/src/model/loadGraph.ts
CHANGED
|
@@ -96,7 +96,7 @@ export function loadGraph(
|
|
|
96
96
|
* on disk may be from any build — so the version is the first question to ask
|
|
97
97
|
* of it.
|
|
98
98
|
*/
|
|
99
|
-
function parseDump(json: string): ITtscGraphDump {
|
|
99
|
+
export function parseDump(json: string): ITtscGraphDump {
|
|
100
100
|
let value: unknown;
|
|
101
101
|
try {
|
|
102
102
|
value = JSON.parse(json);
|
|
@@ -121,5 +121,14 @@ function parseDump(json: string): ITtscGraphDump {
|
|
|
121
121
|
"project, or from TTSC_GRAPH_BINARY).",
|
|
122
122
|
);
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
try {
|
|
125
|
+
return typia.assert<ITtscGraphDump>(value);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`@ttsc/graph: dump output does not match schema v${String(
|
|
129
|
+
DUMP_SCHEMA_VERSION,
|
|
130
|
+
)}: ${error instanceof Error ? error.message : String(error)}`,
|
|
131
|
+
{ cause: error },
|
|
132
|
+
);
|
|
133
|
+
}
|
|
125
134
|
}
|
package/src/reduce.ts
CHANGED
|
@@ -62,11 +62,6 @@ function isAbsolute(p: string): boolean {
|
|
|
62
62
|
return /^(?:[A-Za-z]:)?\//.test(posix(p));
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
function isWindowsPath(p: string): boolean {
|
|
66
|
-
const normalized = posix(p);
|
|
67
|
-
return /^[A-Za-z]:(?:\/|$)/.test(normalized) || normalized.startsWith("//");
|
|
68
|
-
}
|
|
69
|
-
|
|
70
65
|
function directoryOf(file: string): string {
|
|
71
66
|
const normalized = posix(file).replace(/\/+$/, "");
|
|
72
67
|
const slash = normalized.lastIndexOf("/");
|
|
@@ -77,16 +72,13 @@ function directoryOf(file: string): string {
|
|
|
77
72
|
function commonRoot(directories: string[]): string {
|
|
78
73
|
if (directories.length === 0) return "";
|
|
79
74
|
let parts = posix(directories[0]!).split("/");
|
|
80
|
-
const caseInsensitive = directories.every(isWindowsPath);
|
|
81
75
|
for (const directory of directories.slice(1)) {
|
|
82
76
|
const other = posix(directory).split("/");
|
|
83
77
|
let i = 0;
|
|
84
78
|
while (
|
|
85
79
|
i < parts.length &&
|
|
86
80
|
i < other.length &&
|
|
87
|
-
(
|
|
88
|
-
? parts[i]!.toLowerCase() === other[i]!.toLowerCase()
|
|
89
|
-
: parts[i] === other[i])
|
|
81
|
+
legacyPathSegmentsEqual(parts, other, i)
|
|
90
82
|
)
|
|
91
83
|
i++;
|
|
92
84
|
parts = parts.slice(0, i);
|
|
@@ -95,6 +87,41 @@ function commonRoot(directories: string[]): string {
|
|
|
95
87
|
return parts.join("/");
|
|
96
88
|
}
|
|
97
89
|
|
|
90
|
+
function legacyPathSegmentsEqual(
|
|
91
|
+
left: readonly string[],
|
|
92
|
+
right: readonly string[],
|
|
93
|
+
index: number,
|
|
94
|
+
): boolean {
|
|
95
|
+
const leftVolume = windowsVolumeSegmentCount(left);
|
|
96
|
+
const rightVolume = windowsVolumeSegmentCount(right);
|
|
97
|
+
return leftVolume !== 0 && leftVolume === rightVolume && index < leftVolume
|
|
98
|
+
? left[index]!.toLowerCase() === right[index]!.toLowerCase()
|
|
99
|
+
: left[index] === right[index];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function windowsVolumeSegmentCount(parts: readonly string[]): number {
|
|
103
|
+
if (/^[A-Za-z]:$/.test(parts[0] ?? "")) return 1;
|
|
104
|
+
return parts[0] === "" &&
|
|
105
|
+
parts[1] === "" &&
|
|
106
|
+
parts[2] !== undefined &&
|
|
107
|
+
parts[2] !== "" &&
|
|
108
|
+
parts[3] !== undefined &&
|
|
109
|
+
parts[3] !== ""
|
|
110
|
+
? 4
|
|
111
|
+
: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function legacyPathIsWithin(candidate: string, root: string): boolean {
|
|
115
|
+
const candidateParts = posix(candidate).split("/");
|
|
116
|
+
const rootParts = posix(root).split("/");
|
|
117
|
+
return (
|
|
118
|
+
rootParts.length <= candidateParts.length &&
|
|
119
|
+
rootParts.every((_, index) =>
|
|
120
|
+
legacyPathSegmentsEqual(rootParts, candidateParts, index),
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
98
125
|
// A null root means the dump's paths are already project-relative (the current
|
|
99
126
|
// `ttscgraph dump` contract); they pass through structure-intact.
|
|
100
127
|
function relativize(abs: string, root: string | null): string {
|
|
@@ -102,15 +129,7 @@ function relativize(abs: string, root: string | null): string {
|
|
|
102
129
|
if (root === null) return a;
|
|
103
130
|
const normalizedRoot = posix(root);
|
|
104
131
|
const r = normalizedRoot === "/" ? "/" : normalizedRoot.replace(/\/+$/, "");
|
|
105
|
-
|
|
106
|
-
const comparedPath = caseInsensitive ? a.toLowerCase() : a;
|
|
107
|
-
const comparedRoot = caseInsensitive ? r.toLowerCase() : r;
|
|
108
|
-
if (
|
|
109
|
-
comparedRoot &&
|
|
110
|
-
(comparedRoot === "/" ||
|
|
111
|
-
comparedPath === comparedRoot ||
|
|
112
|
-
comparedPath.startsWith(comparedRoot + "/"))
|
|
113
|
-
)
|
|
132
|
+
if (r && (r === "/" || legacyPathIsWithin(a, r)))
|
|
114
133
|
return a.slice(r.length).replace(/^\/+/, "");
|
|
115
134
|
const nm = a.lastIndexOf("node_modules/");
|
|
116
135
|
// A package tail is a deliberate normalization: the same dependency reached
|
package/src/server/runDetails.ts
CHANGED
|
@@ -24,13 +24,11 @@ const MAX_NEIGHBORS = 3;
|
|
|
24
24
|
const DEFAULT_DEPENDENCIES = 2;
|
|
25
25
|
const MAX_DEPENDENCIES = 4;
|
|
26
26
|
// Structural relationships are navigation, not the dependency picture details is for.
|
|
27
|
-
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"
|
|
27
|
+
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"]);
|
|
28
28
|
// Kinds whose value is their member outline, not implementation text.
|
|
29
|
-
const CONTAINER_KINDS = new Set<
|
|
29
|
+
const CONTAINER_KINDS = new Set<ITtscGraphNode["kind"]>([
|
|
30
30
|
"class",
|
|
31
31
|
"interface",
|
|
32
|
-
"namespace",
|
|
33
|
-
"module",
|
|
34
32
|
"enum",
|
|
35
33
|
"file",
|
|
36
34
|
]);
|
|
@@ -464,16 +462,13 @@ function edgeKindRank(kind: string): number {
|
|
|
464
462
|
case "accesses":
|
|
465
463
|
case "renders":
|
|
466
464
|
return 2;
|
|
467
|
-
case "tests":
|
|
468
|
-
return 3;
|
|
469
465
|
case "overrides":
|
|
470
|
-
|
|
471
|
-
return 4;
|
|
466
|
+
return 3;
|
|
472
467
|
case "extends":
|
|
473
468
|
case "implements":
|
|
474
|
-
return
|
|
469
|
+
return 4;
|
|
475
470
|
case "type_ref":
|
|
476
|
-
return
|
|
471
|
+
return 5;
|
|
477
472
|
default:
|
|
478
473
|
return 10;
|
|
479
474
|
}
|
|
@@ -12,7 +12,7 @@ const MAX_LIMIT = 8;
|
|
|
12
12
|
const DEFAULT_NEIGHBORS = 0;
|
|
13
13
|
const MAX_NEIGHBORS = 2;
|
|
14
14
|
const MAX_SEEDS = 3;
|
|
15
|
-
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"
|
|
15
|
+
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"]);
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Build the first source-free entrypoints list for a code question. The result
|
|
@@ -198,16 +198,13 @@ function edgeKindRank(kind: string): number {
|
|
|
198
198
|
case "accesses":
|
|
199
199
|
case "renders":
|
|
200
200
|
return 2;
|
|
201
|
-
case "tests":
|
|
202
|
-
return 3;
|
|
203
201
|
case "overrides":
|
|
204
|
-
|
|
205
|
-
return 4;
|
|
202
|
+
return 3;
|
|
206
203
|
case "extends":
|
|
207
204
|
case "implements":
|
|
208
|
-
return
|
|
205
|
+
return 4;
|
|
209
206
|
case "type_ref":
|
|
210
|
-
return
|
|
207
|
+
return 5;
|
|
211
208
|
default:
|
|
212
209
|
return 10;
|
|
213
210
|
}
|
package/src/server/runLookup.ts
CHANGED
|
@@ -260,7 +260,7 @@ function degree(graph: TtscGraphMemory, id: string): number {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
function isStructural(kind: string): boolean {
|
|
263
|
-
return kind === "contains" || kind === "exports"
|
|
263
|
+
return kind === "contains" || kind === "exports";
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
function isTestFile(file: string): boolean {
|
|
@@ -5,7 +5,7 @@ import { isPublicApiNoisePath, isSupportPath } from "./pathPolicy";
|
|
|
5
5
|
import { IRunnerOutput, resultNext } from "./resultNext";
|
|
6
6
|
|
|
7
7
|
/** Edges that express nesting/packaging, not code dependency. */
|
|
8
|
-
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"
|
|
8
|
+
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"]);
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Project a compact, source-read-free architecture map: counts by kind, folder
|
|
@@ -83,8 +83,8 @@ function layers(graph: TtscGraphMemory): ITtscGraphOverview.ILayer[] {
|
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* The symbols at the center of the dependency graph, ranked by real fan-in and
|
|
86
|
-
* fan-out. Structural `contains`/`exports
|
|
87
|
-
*
|
|
86
|
+
* fan-out. Structural `contains`/`exports` edges are excluded so the ranking
|
|
87
|
+
* reflects code dependency, not nesting.
|
|
88
88
|
*/
|
|
89
89
|
function hotspots(graph: TtscGraphMemory): ITtscGraphOverview.IHotspot[] {
|
|
90
90
|
const real = (id: string, side: "in" | "out"): number => {
|
package/src/server/runTour.ts
CHANGED
|
@@ -49,21 +49,19 @@ const MAX_READ_NEXT = 14;
|
|
|
49
49
|
const FLOW_OVERLAP = 0.6;
|
|
50
50
|
const TOUR_TRACE_MAX_DEPTH = 6;
|
|
51
51
|
const TOUR_TRACE_MAX_NODES = 18;
|
|
52
|
-
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"
|
|
52
|
+
const STRUCTURAL_KINDS = new Set<string>(["contains", "exports"]);
|
|
53
53
|
const EXECUTION_KINDS = new Set<string>([
|
|
54
54
|
"calls",
|
|
55
55
|
"instantiates",
|
|
56
56
|
"accesses",
|
|
57
57
|
"renders",
|
|
58
58
|
]);
|
|
59
|
-
const TOUR_SEED_KINDS = new Set<
|
|
59
|
+
const TOUR_SEED_KINDS = new Set<ITtscGraphNode["kind"]>([
|
|
60
60
|
"class",
|
|
61
61
|
"function",
|
|
62
62
|
"method",
|
|
63
63
|
"property",
|
|
64
64
|
"variable",
|
|
65
|
-
"module",
|
|
66
|
-
"namespace",
|
|
67
65
|
"enum",
|
|
68
66
|
]);
|
|
69
67
|
|
|
@@ -845,11 +843,11 @@ function isTourHop(graph: TtscGraphMemory, hop: ITtscGraphTrace.IHop): boolean {
|
|
|
845
843
|
// floor makes it a no-op on small graphs that have no genuine hub. The `out <= 1`
|
|
846
844
|
// guard keeps thin pass-throughs out but never prunes a real branching step.
|
|
847
845
|
//
|
|
848
|
-
// The fan-in counted is EXECUTION fan-in. `realDegree` also counts `type_ref
|
|
849
|
-
//
|
|
850
|
-
//
|
|
851
|
-
//
|
|
852
|
-
//
|
|
846
|
+
// The fan-in counted is EXECUTION fan-in. `realDegree` also counts `type_ref`
|
|
847
|
+
// and `extends`, and a widely referenced type is not a call hub: a `Config`
|
|
848
|
+
// class named in twelve parameter positions and constructed once was read as a
|
|
849
|
+
// hub and cut out of the flow that constructs it. Popularity as a name is not
|
|
850
|
+
// popularity as a call.
|
|
853
851
|
function isSharedUtility(graph: TtscGraphMemory, id: string): boolean {
|
|
854
852
|
const execution = executionDegree(graph, id);
|
|
855
853
|
return execution.in >= 12 && execution.out <= 1;
|
|
@@ -932,11 +930,7 @@ function ownerOf(graph: TtscGraphMemory, id: string): string | undefined {
|
|
|
932
930
|
for (const edge of graph.incoming(id)) {
|
|
933
931
|
if (edge.kind !== "contains") continue;
|
|
934
932
|
const owner = graph.node(edge.from);
|
|
935
|
-
if (
|
|
936
|
-
owner !== undefined &&
|
|
937
|
-
owner.kind !== "file" &&
|
|
938
|
-
owner.kind !== "module"
|
|
939
|
-
) {
|
|
933
|
+
if (owner !== undefined && owner.kind !== "file") {
|
|
940
934
|
return owner.id;
|
|
941
935
|
}
|
|
942
936
|
}
|
package/src/server/runTrace.ts
CHANGED
|
@@ -29,11 +29,7 @@ const MAX_STEPS = 12;
|
|
|
29
29
|
const DISPATCH_KINDS = new Set<string>(["overrides", "implements"]);
|
|
30
30
|
// A declaration whose kind is a type surface never carries a body, and an
|
|
31
31
|
// external leaf carries one the graph deliberately does not hold.
|
|
32
|
-
const BODYLESS_KINDS = new Set<string>([
|
|
33
|
-
"interface",
|
|
34
|
-
"type",
|
|
35
|
-
"external_symbol",
|
|
36
|
-
]);
|
|
32
|
+
const BODYLESS_KINDS = new Set<string>(["interface", "type"]);
|
|
37
33
|
// `abstract` and `declare` are the two keywords that take the body away from a
|
|
38
34
|
// declaration that would otherwise have to have one.
|
|
39
35
|
const BODYLESS_MODIFIERS = new Set<string>(["abstract", "declare"]);
|
|
@@ -48,11 +44,10 @@ const BODYLESS_MODIFIERS = new Set<string>(["abstract", "declare"]);
|
|
|
48
44
|
const DISPATCH_HUB = 12;
|
|
49
45
|
|
|
50
46
|
/**
|
|
51
|
-
* Breadth-first trace along the dependency graph. Structural
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* surface is legible.
|
|
47
|
+
* Breadth-first trace along the dependency graph. Structural (contains/exports)
|
|
48
|
+
* edges are excluded so the path is real call/type flow; forward walks callees,
|
|
49
|
+
* reverse and impact walk callers. Impact additionally tags each reached node's
|
|
50
|
+
* role so the blast radius on the public surface is legible.
|
|
56
51
|
*/
|
|
57
52
|
export function runTrace(
|
|
58
53
|
graph: TtscGraphMemory,
|
|
@@ -946,7 +941,7 @@ function traversable(
|
|
|
946
941
|
kind: string,
|
|
947
942
|
focus: ITtscGraphTrace.IRequest["focus"],
|
|
948
943
|
): boolean {
|
|
949
|
-
if (kind === "contains" || kind === "exports"
|
|
944
|
+
if (kind === "contains" || kind === "exports") {
|
|
950
945
|
return false;
|
|
951
946
|
}
|
|
952
947
|
if (kind === "dispatches") return focus !== "types";
|
|
@@ -963,8 +958,7 @@ function traversable(
|
|
|
963
958
|
kind === "type_ref" ||
|
|
964
959
|
kind === "extends" ||
|
|
965
960
|
kind === "implements" ||
|
|
966
|
-
kind === "overrides"
|
|
967
|
-
kind === "decorates"
|
|
961
|
+
kind === "overrides"
|
|
968
962
|
);
|
|
969
963
|
}
|
|
970
964
|
return true;
|
|
@@ -985,16 +979,13 @@ function edgeKindRank(kind: string): number {
|
|
|
985
979
|
return 2;
|
|
986
980
|
case "accesses":
|
|
987
981
|
return 3;
|
|
988
|
-
case "tests":
|
|
989
|
-
return 4;
|
|
990
982
|
case "overrides":
|
|
991
|
-
|
|
992
|
-
return 5;
|
|
983
|
+
return 4;
|
|
993
984
|
case "extends":
|
|
994
985
|
case "implements":
|
|
995
|
-
return
|
|
986
|
+
return 5;
|
|
996
987
|
case "type_ref":
|
|
997
|
-
return
|
|
988
|
+
return 6;
|
|
998
989
|
default:
|
|
999
990
|
return 10;
|
|
1000
991
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ITtscGraphEdge } from "./ITtscGraphEdge";
|
|
2
2
|
import { ITtscGraphNode } from "./ITtscGraphNode";
|
|
3
3
|
import { ITtscGraphSpan } from "./ITtscGraphSpan";
|
|
4
|
+
import { TtscGraphDumpEdgeKind } from "./TtscGraphDumpEdgeKind";
|
|
5
|
+
import { TtscGraphDumpNodeKind } from "./TtscGraphDumpNodeKind";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* The whole-graph export `ttscgraph dump` writes and the MCP server loads — the
|
|
@@ -229,8 +231,11 @@ export namespace ITtscGraphDump {
|
|
|
229
231
|
*/
|
|
230
232
|
export interface INode extends Omit<
|
|
231
233
|
ITtscGraphNode,
|
|
232
|
-
"evidence" | "implementation"
|
|
234
|
+
"evidence" | "implementation" | "kind"
|
|
233
235
|
> {
|
|
236
|
+
/** Declaration kind written by the native producer. */
|
|
237
|
+
kind: TtscGraphDumpNodeKind;
|
|
238
|
+
|
|
234
239
|
/** Declaration span; its file is this node's `file`. */
|
|
235
240
|
evidence?: ITtscGraphSpan;
|
|
236
241
|
|
|
@@ -246,7 +251,10 @@ export namespace ITtscGraphDump {
|
|
|
246
251
|
* names — the id is `path#Qualified.Name:kind` — so the path rode the wire a
|
|
247
252
|
* second time on every edge, and edges outnumber nodes several times over.
|
|
248
253
|
*/
|
|
249
|
-
export interface IEdge extends Omit<ITtscGraphEdge, "evidence"> {
|
|
254
|
+
export interface IEdge extends Omit<ITtscGraphEdge, "evidence" | "kind"> {
|
|
255
|
+
/** Relationship kind written by the native producer. */
|
|
256
|
+
kind: TtscGraphDumpEdgeKind;
|
|
257
|
+
|
|
250
258
|
/** Expression span; its file is the one embedded in `from`. */
|
|
251
259
|
evidence?: ITtscGraphSpan;
|
|
252
260
|
}
|
|
@@ -4,8 +4,7 @@ import { TtscGraphNodeKind } from "./TtscGraphNodeKind";
|
|
|
4
4
|
import { TtscGraphNodeModifier } from "./TtscGraphNodeModifier";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* One node in the graph: a declared symbol or a
|
|
8
|
-
* package).
|
|
7
|
+
* One node in the graph: a declared symbol or a synthesized file container.
|
|
9
8
|
*
|
|
10
9
|
* The `id` is position-invariant: `path#qualifiedName:kind` (e.g.
|
|
11
10
|
* `src/order.ts#OrderService.create:method`), so inserting a line above a
|
|
@@ -44,11 +44,52 @@ export interface ITtscGraphSnapshot {
|
|
|
44
44
|
/** The snapshot, present exactly when `changed` is true. */
|
|
45
45
|
dump?: ITtscGraphDump;
|
|
46
46
|
|
|
47
|
+
/** Native graph-shard transaction, present for an opted-in changed request. */
|
|
48
|
+
snapshot?: ITtscGraphSnapshot.ITransaction;
|
|
49
|
+
|
|
47
50
|
/** Set when the request produced no snapshot; `mode` is then `"error"`. */
|
|
48
51
|
error?: string;
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
export namespace ITtscGraphSnapshot {
|
|
55
|
+
/** Versioned content-addressed transaction emitted by `ttscgraph serve`. */
|
|
56
|
+
export interface ITransaction {
|
|
57
|
+
protocolVersion: number;
|
|
58
|
+
schemaVersion: number;
|
|
59
|
+
project: string;
|
|
60
|
+
tsconfig: string;
|
|
61
|
+
producer: ITtscGraphDump.IProducer;
|
|
62
|
+
capabilities: string[];
|
|
63
|
+
universe: ITtscGraphDump.IUniverse;
|
|
64
|
+
sequence: number;
|
|
65
|
+
generation: string;
|
|
66
|
+
baseSequence?: number;
|
|
67
|
+
baseGeneration?: string;
|
|
68
|
+
upserts: IShardUpsert[];
|
|
69
|
+
deletes: string[];
|
|
70
|
+
manifest: IShardReference[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface IShardUpsert {
|
|
74
|
+
digest: string;
|
|
75
|
+
shard: IShard;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface IShardReference {
|
|
79
|
+
key: string;
|
|
80
|
+
digest: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** One source/config or metadata-owned raw compiler fact shard. */
|
|
84
|
+
export interface IShard {
|
|
85
|
+
key: string;
|
|
86
|
+
source?: ITtscGraphDump.ISourceDigest;
|
|
87
|
+
config?: ITtscGraphDump.IFileDigest;
|
|
88
|
+
nodes: ITtscGraphDump.INode[];
|
|
89
|
+
edges: ITtscGraphDump.IEdge[];
|
|
90
|
+
diagnostics: ITtscGraphDump.IDiagnostic[];
|
|
91
|
+
}
|
|
92
|
+
|
|
52
93
|
/**
|
|
53
94
|
* The computation modes the producer reports, plus the transport's `error`.
|
|
54
95
|
*
|
|
@@ -56,7 +97,7 @@ export namespace ITtscGraphSnapshot {
|
|
|
56
97
|
* - `reload`: the build universe moved, so the program was reloaded whole.
|
|
57
98
|
* - `unchanged`: nothing moved; no dump rides it and the last one still holds.
|
|
58
99
|
* - `incremental`: edits applied onto the reused resident program.
|
|
59
|
-
* - `rebuild`: edits applied, but
|
|
100
|
+
* - `rebuild`: edits applied, but graph projection required a complete build.
|
|
60
101
|
* - `error`: no snapshot was produced.
|
|
61
102
|
*/
|
|
62
103
|
export type Mode =
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The relationship a directed edge encodes between two {@link ITtscGraphNode}s.
|
|
3
3
|
*
|
|
4
|
-
* Structural
|
|
5
|
-
*
|
|
4
|
+
* Structural `exports` edges come from the native declaration pass, while the
|
|
5
|
+
* TypeScript memory layer synthesizes `contains` ownership. Value and type
|
|
6
|
+
* edges (`calls`, `accesses`, `instantiates`, `type_ref`,
|
|
6
7
|
* `extends`, `implements`, `overrides`, `renders`) are resolved by the checker
|
|
7
|
-
* — `renders` is a JSX component use.
|
|
8
|
-
*
|
|
8
|
+
* — `renders` is a JSX component use. Decorators are facts on their target node,
|
|
9
|
+
* not edges.
|
|
9
10
|
*
|
|
10
11
|
* `dispatches` is the runtime counterpart of `overrides`/`implements`: the
|
|
11
12
|
* checker resolves a call to the declaration it names, and where that
|
|
12
13
|
* declaration is abstract or an interface member, the code that runs is its
|
|
13
14
|
* implementation. It carries the implementation's declaration span, and a
|
|
14
|
-
* traversal that follows what executes emits it in place of the dead end.
|
|
15
|
+
* traversal that follows what executes emits it in place of the dead end. It is
|
|
16
|
+
* trace-only and never appears in a native dump.
|
|
15
17
|
*/
|
|
16
18
|
export type TtscGraphEdgeKind =
|
|
17
19
|
| "contains"
|
|
18
20
|
| "exports"
|
|
19
|
-
| "imports"
|
|
20
21
|
| "calls"
|
|
21
22
|
| "accesses"
|
|
22
23
|
| "instantiates"
|
|
@@ -25,6 +26,4 @@ export type TtscGraphEdgeKind =
|
|
|
25
26
|
| "implements"
|
|
26
27
|
| "overrides"
|
|
27
28
|
| "dispatches"
|
|
28
|
-
| "
|
|
29
|
-
| "renders"
|
|
30
|
-
| "tests";
|
|
29
|
+
| "renders";
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What a graph node represents.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* internals.
|
|
4
|
+
* The native builder emits declaration kinds from `module` through `method`.
|
|
5
|
+
* The TypeScript memory layer replaces each module with a `file` container and
|
|
6
|
+
* refines class/interface member variables to `property`. An external boundary
|
|
7
|
+
* leaf keeps its real declaration kind and sets `external: true`.
|
|
9
8
|
*
|
|
10
9
|
* Used as the `kind` discriminant on {@link ITtscGraphNode}.
|
|
11
10
|
*/
|
|
12
11
|
export type TtscGraphNodeKind =
|
|
13
12
|
| "file"
|
|
14
|
-
| "package"
|
|
15
|
-
| "namespace"
|
|
16
|
-
| "module"
|
|
17
13
|
| "function"
|
|
18
14
|
| "class"
|
|
19
15
|
| "interface"
|
|
@@ -21,6 +17,4 @@ export type TtscGraphNodeKind =
|
|
|
21
17
|
| "enum"
|
|
22
18
|
| "variable"
|
|
23
19
|
| "method"
|
|
24
|
-
| "property"
|
|
25
|
-
| "parameter"
|
|
26
|
-
| "external_symbol";
|
|
20
|
+
| "property";
|
package/src/structures/index.ts
CHANGED
|
@@ -21,5 +21,7 @@ export * from "./ITtscGraphSnapshot";
|
|
|
21
21
|
export * from "./ITtscGraphSpan";
|
|
22
22
|
export * from "./ITtscGraphTour";
|
|
23
23
|
export * from "./TtscGraphEdgeKind";
|
|
24
|
+
export * from "./TtscGraphDumpEdgeKind";
|
|
25
|
+
export * from "./TtscGraphDumpNodeKind";
|
|
24
26
|
export * from "./TtscGraphNodeKind";
|
|
25
27
|
export * from "./TtscGraphNodeModifier";
|
package/src/view.ts
CHANGED
|
@@ -9,8 +9,9 @@ import {
|
|
|
9
9
|
positiveIntegerOption,
|
|
10
10
|
projectOptions,
|
|
11
11
|
} from "./launcherArgs";
|
|
12
|
+
import { parseDump } from "./model/loadGraph";
|
|
12
13
|
import { ensureExecutable } from "./nativeExecutable";
|
|
13
|
-
import {
|
|
14
|
+
import { reduce } from "./reduce";
|
|
14
15
|
import { resolveGraphBinary } from "./resolveGraphBinary";
|
|
15
16
|
|
|
16
17
|
interface ViewOptions {
|
|
@@ -83,12 +84,15 @@ export function runView(argv: readonly string[]): number | void {
|
|
|
83
84
|
return dump.status ?? 1;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
let raw:
|
|
87
|
+
let raw: ReturnType<typeof parseDump>;
|
|
87
88
|
try {
|
|
88
|
-
raw =
|
|
89
|
-
} catch (
|
|
89
|
+
raw = parseDump(dump.stdout);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
92
|
process.stderr.write(
|
|
91
|
-
|
|
93
|
+
message.startsWith("@ttsc/graph:")
|
|
94
|
+
? `${message}\n`
|
|
95
|
+
: `@ttsc/graph: could not validate the graph dump: ${message}\n`,
|
|
92
96
|
);
|
|
93
97
|
return 1;
|
|
94
98
|
}
|
|
@@ -127,10 +131,30 @@ export function runView(argv: readonly string[]): number | void {
|
|
|
127
131
|
}
|
|
128
132
|
});
|
|
129
133
|
|
|
134
|
+
let endpoint = `127.0.0.1:${opts.port}`;
|
|
135
|
+
let failed = false;
|
|
136
|
+
server.on("error", (error: NodeJS.ErrnoException) => {
|
|
137
|
+
if (failed) return;
|
|
138
|
+
failed = true;
|
|
139
|
+
const detail =
|
|
140
|
+
typeof error.code === "string"
|
|
141
|
+
? `${error.code}: ${error.message}`
|
|
142
|
+
: error.message;
|
|
143
|
+
process.stderr.write(
|
|
144
|
+
`@ttsc/graph: could not serve the 3D viewer at ${endpoint} (${detail}).\n`,
|
|
145
|
+
);
|
|
146
|
+
process.exitCode = 1;
|
|
147
|
+
if (server.listening) {
|
|
148
|
+
server.close();
|
|
149
|
+
server.closeAllConnections?.();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
130
152
|
server.listen(opts.port, "127.0.0.1", () => {
|
|
153
|
+
if (failed) return;
|
|
131
154
|
const address = server.address();
|
|
132
155
|
const port =
|
|
133
156
|
typeof address === "object" && address ? address.port : opts.port;
|
|
157
|
+
endpoint = `127.0.0.1:${port}`;
|
|
134
158
|
const url = `http://127.0.0.1:${port}/`;
|
|
135
159
|
const counts = payload.counts;
|
|
136
160
|
process.stderr.write(
|